From e5bd09f22b3cd47b5692d4288aea3c3cf1cfffa6 Mon Sep 17 00:00:00 2001 From: Tristan Simas Date: Mon, 5 Oct 2026 09:59:55 -0400 Subject: [PATCH 1/4] docs: scope original native damage interval reduction --- .../SCOPE.md | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 evidence/native-damage-band-reduction-20261005/SCOPE.md diff --git a/evidence/native-damage-band-reduction-20261005/SCOPE.md b/evidence/native-damage-band-reduction-20261005/SCOPE.md new file mode 100644 index 0000000000..2a94c9c067 --- /dev/null +++ b/evidence/native-damage-band-reduction-20261005/SCOPE.md @@ -0,0 +1,25 @@ +# Reduce original damage intervals once per vertical band + +Base: merged Text68 dd625069e50cc0b909832b549a485abc55067149. + +The existing `Compositor._regions_to_spans` expands every damaged rectangle +into one interval per row, then sorts and merges the same active intervals +again on each row. Output rows are necessary; repeated interval acquisition +and union inside a band without rectangle edges is unnecessary work. + +Keep that original reducer and its exact ordered output. Use rectangle start +and end edges to reduce active horizontal intervals once per changing vertical +band, then emit those original spans on each required row. Preserve overlap +multiplicity, touching intervals, gaps, negative origins and complete bounds. +No new retained resource, cache, map, timer, flag, type, API or Toad caller. + +The complete Package AST before/after from Text68 is retained as caller +source evidence: native249/tests460 and Toad288/tests397/tools41 with0parse +omissions. The current reducer has four native callers and seven test callers; +all render paths continue to use the same owner. Dynamic private-method +rebinding and unregistered external callers remain unproved. + +Source reasoning and coherent implementation first; final proportionate +interval and affected native App controls last. Native68 original14+2 controls +and negative are immutable, not rerun. No build/installed prefix/SDK/provider/ +media purpose; no measured gain or physical cadence claim. From 83ea6b17eab3ac0a917451070a954b7299a1a91a Mon Sep 17 00:00:00 2001 From: Tristan Simas Date: Mon, 5 Oct 2026 10:01:00 -0400 Subject: [PATCH 2/4] perf: reduce native damage intervals at their original vertical edges --- src/textual/_compositor.py | 46 +++++++++++++++++++------------------- 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/src/textual/_compositor.py b/src/textual/_compositor.py index d0a851c829..5e1057d58d 100644 --- a/src/textual/_compositor.py +++ b/src/textual/_compositor.py @@ -15,6 +15,7 @@ from dataclasses import dataclass from abc import ABC, abstractmethod from bisect import bisect_left, bisect_right +from collections import Counter from contextlib import contextmanager from functools import cached_property from types import MappingProxyType @@ -636,30 +637,29 @@ def _regions_to_spans( Returns: Yields tuples of (Y, X1, X2). """ - inline_ranges: dict[int, list[tuple[int, int]]] = {} - setdefault = inline_ranges.setdefault + edges: dict[int, Counter[tuple[int, int]]] = {} for region_x, region_y, width, height in regions: - span = (region_x, region_x + width) - for y in range(region_y, region_y + height): - setdefault(y, []).append(span) - - slice_remaining = slice(1, None) - for y, ranges in sorted(inline_ranges.items()): - if len(ranges) == 1: - # Special case of 1 span - yield (y, *ranges[0]) - else: - ranges.sort() - x1, x2 = ranges[0] - for next_x1, next_x2 in ranges[slice_remaining]: - if next_x1 <= x2: - if next_x2 > x2: - x2 = next_x2 - else: - yield (y, x1, x2) - x1 = next_x1 - x2 = next_x2 - yield (y, x1, x2) + if height > 0: + span = (region_x, region_x + width) + edges.setdefault(region_y, Counter())[span] += 1 + edges.setdefault(region_y + height, Counter())[span] -= 1 + + active: Counter[tuple[int, int]] = Counter() + spans: list[tuple[int, int]] = [] + previous_y = 0 + for y, changes in sorted(edges.items()): + if spans: + for row in range(previous_y, y): + for span in spans: + yield (row, *span) + active.update(changes) + spans = [] + for x1, x2 in sorted(active.elements()): + if spans and x1 <= spans[-1][1]: + spans[-1] = (spans[-1][0], max(x2, spans[-1][1])) + else: + spans.append((x1, x2)) + previous_y = y def __rich_repr__(self) -> rich.repr.Result: yield "size", self.size From 48f79749ef7521ff727cbc7be0feba1794e99c6a Mon Sep 17 00:00:00 2001 From: Tristan Simas Date: Mon, 5 Oct 2026 10:02:30 -0400 Subject: [PATCH 3/4] test: retain original damage band and overlap contracts --- tests/test_compositor_regions_to_spans.py | 24 +++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/tests/test_compositor_regions_to_spans.py b/tests/test_compositor_regions_to_spans.py index 31de88ad52..5f26044623 100644 --- a/tests/test_compositor_regions_to_spans.py +++ b/tests/test_compositor_regions_to_spans.py @@ -52,3 +52,27 @@ def test_regions_to_ranges_directly_adjacent_ranges_merged(): (0, 0, 2), (1, 0, 2), ] + + +def test_damage_bands_keep_overlap_multiplicity_origins_and_empty_gaps(): + regions = [ + Region(-2, -3, 4, 4), + Region(-2, -1, 4, 4), + Region(2, -2, 2, 4), + Region(9, 0, 2, 2), + Region(3, 5, 1, 0), + Region(6, 7, 1, -1), + Region(100, 1000, 0, 1), + ] + # Equal horizontal intervals are independently owned rectangles: one can + # end while the other still supplies damage. A one-shot input and distant + # band retain the original reducer's order and zero-width span contract. + assert list(Compositor._regions_to_spans(iter(regions))) == [ + (-3, -2, 2), + (-2, -2, 4), + (-1, -2, 4), + (0, -2, 4), (0, 9, 11), + (1, -2, 4), (1, 9, 11), + (2, -2, 2), + (1000, 100, 100), + ] From 2f8cc5209338a158aa09073de8a0d17aa33b10c7 Mon Sep 17 00:00:00 2001 From: Tristan Simas Date: Mon, 5 Oct 2026 10:04:41 -0400 Subject: [PATCH 4/4] docs: qualify original damage band reduction at pure source scope --- .../SCOPE.md | 37 +++++++ .../owner-consumers.json | 99 +++++++++++++++++++ .../source-interval01.json | 18 ++++ .../source-interval01.log | 2 + 4 files changed, 156 insertions(+) create mode 100644 evidence/native-damage-band-reduction-20261005/owner-consumers.json create mode 100644 evidence/native-damage-band-reduction-20261005/source-interval01.json create mode 100644 evidence/native-damage-band-reduction-20261005/source-interval01.log diff --git a/evidence/native-damage-band-reduction-20261005/SCOPE.md b/evidence/native-damage-band-reduction-20261005/SCOPE.md index 2a94c9c067..80f6f9c47a 100644 --- a/evidence/native-damage-band-reduction-20261005/SCOPE.md +++ b/evidence/native-damage-band-reduction-20261005/SCOPE.md @@ -23,3 +23,40 @@ Source reasoning and coherent implementation first; final proportionate interval and affected native App controls last. Native68 original14+2 controls and negative are immutable, not rerun. No build/installed prefix/SDK/provider/ media purpose; no measured gain or physical cadence claim. + +## Published algorithm and caller closure + +Production83ea6b17eab3ac0a917451070a954b7299a1a91a, one file23+/23-. +The original reducer now consumes each input rectangle once into its vertical +start/end edge deltas. Counter preserves original overlapping interval +multiplicity; intervals are reduced only when that active set changes at a +vertical edge. Each required output row still receives the original ordered +spans. Empty gaps are skipped and no source rectangle is expanded into a +per-row temporary range list. Counter.elements preserves original interval +multiplicity before merging; no retained state survives the generator. + +`owner-consumers.json`: actual source48f79749ef7521ff727cbc7be0feba1794e99c6a, +production249/tests460, parse omissions0, one reducer declaration and four +native consumers. Original Text68 pinned Toad dependency census is retained; +all source signatures and related consumers remain unchanged here. Existing +full, partial, inline/export and original-body capture paths use this reducer. +One source fact remains one owner; no separate fast path or uncached reducer. + +## Final pure source controls + +ONE batch8PASS/0.18s, controller1.3053654759423807s, exit0. Source48f79749e, +production83ea6b17e. Exact logSHA +`c513a336404899b0e4e59540faac60f730d81e38e236b1b942334605c26575ed`. +Existing empty/single/partial/full overlap, different-row and same-row gaps, +and adjacent intervals passed. The added whole-contract case preserves +different rectangle lifetimes with equal horizontal spans, negative origins, +one-shot input, distant empty gaps, nonpositive height and original +zero-width span output. No App, installed prefix, SDK/native process, build, +provider, media or accepted Native68 controls were run. + +Draft / pure source-qualified only. No installed frame/materialization or +physical scrolling result is inferred. Full observed source/paint workflow +acceptance belongs to the next meaningful joined Heis cohort; issued46067 is +unchanged. CPU time and cadence are unmeasured. Band reduction still sorts +active intervals at each real source edge; it does not remove legitimate +output rows or promise a universally faster result for every damage shape. diff --git a/evidence/native-damage-band-reduction-20261005/owner-consumers.json b/evidence/native-damage-band-reduction-20261005/owner-consumers.json new file mode 100644 index 0000000000..4a6d791041 --- /dev/null +++ b/evidence/native-damage-band-reduction-20261005/owner-consumers.json @@ -0,0 +1,99 @@ +{ + "base": "dd625069e50cc0b909832b549a485abc55067149", + "source": "48f79749ef7521ff727cbc7be0feba1794e99c6a", + "before_dependency_evidence": "evidence/native-damage-span-admission-20261005/before.json", + "before_after_native_evidence": "evidence/native-damage-span-admission-20261005/after.json", + "roots": [ + { + "root": "src/textual", + "parsed": 249, + "omitted": [] + }, + { + "root": "tests", + "parsed": 460, + "omitted": [] + } + ], + "sites": [ + { + "path": "src/textual/_compositor.py", + "line": 628, + "node": "FunctionDef", + "source": " def _regions_to_spans(" + }, + { + "path": "src/textual/_compositor.py", + "line": 1559, + "node": "Attribute", + "source": "self._regions_to_spans" + }, + { + "path": "src/textual/_compositor.py", + "line": 1588, + "node": "Attribute", + "source": "self._regions_to_spans" + }, + { + "path": "src/textual/_compositor.py", + "line": 1607, + "node": "Attribute", + "source": "self._regions_to_spans" + }, + { + "path": "src/textual/_compositor.py", + "line": 1647, + "node": "Attribute", + "source": "self._regions_to_spans" + }, + { + "path": "tests/test_compositor_regions_to_spans.py", + "line": 6, + "node": "Attribute", + "source": "Compositor._regions_to_spans" + }, + { + "path": "tests/test_compositor_regions_to_spans.py", + "line": 11, + "node": "Attribute", + "source": "Compositor._regions_to_spans" + }, + { + "path": "tests/test_compositor_regions_to_spans.py", + "line": 19, + "node": "Attribute", + "source": "Compositor._regions_to_spans" + }, + { + "path": "tests/test_compositor_regions_to_spans.py", + "line": 28, + "node": "Attribute", + "source": "Compositor._regions_to_spans" + }, + { + "path": "tests/test_compositor_regions_to_spans.py", + "line": 37, + "node": "Attribute", + "source": "Compositor._regions_to_spans" + }, + { + "path": "tests/test_compositor_regions_to_spans.py", + "line": 42, + "node": "Attribute", + "source": "Compositor._regions_to_spans" + }, + { + "path": "tests/test_compositor_regions_to_spans.py", + "line": 51, + "node": "Attribute", + "source": "Compositor._regions_to_spans" + }, + { + "path": "tests/test_compositor_regions_to_spans.py", + "line": 70, + "node": "Attribute", + "source": "Compositor._regions_to_spans" + } + ], + "limits": "Native AST method/name/attribute ownership evidence; original pinned Toad production/tests/tools relation retained. No dynamic rebinding or external private consumers inferred." +} diff --git a/evidence/native-damage-band-reduction-20261005/source-interval01.json b/evidence/native-damage-band-reduction-20261005/source-interval01.json new file mode 100644 index 0000000000..f084ab0281 --- /dev/null +++ b/evidence/native-damage-band-reduction-20261005/source-interval01.json @@ -0,0 +1,18 @@ +{ + "source": "48f79749ef7521ff727cbc7be0feba1794e99c6a", + "production_source": "83ea6b17eab3ac0a917451070a954b7299a1a91a", + "argv": [ + "/usr/bin/python", + "-m", + "pytest", + "-q", + "tests/test_compositor_regions_to_spans.py" + ], + "seconds": 1.3053654759423807, + "exit_code": 0, + "log": "/home/ts/wt/textual-native-subtree-strips-20261002/.artifacts/native-damage-band-reduction-20261005/source-interval01.log", + "log_sha256": "c513a336404899b0e4e59540faac60f730d81e38e236b1b942334605c26575ed", + "scope": "ONE final pure native interval reducer batch after coherent implementation; no App/installed process/SDK/build/media/provider. Native68 App controls not repeated.", + "installed_qualified": false, + "retained_log": "evidence/native-damage-band-reduction-20261005/source-interval01.log" +} diff --git a/evidence/native-damage-band-reduction-20261005/source-interval01.log b/evidence/native-damage-band-reduction-20261005/source-interval01.log new file mode 100644 index 0000000000..7d31c47749 --- /dev/null +++ b/evidence/native-damage-band-reduction-20261005/source-interval01.log @@ -0,0 +1,2 @@ +........ [100%] +8 passed in 0.18s