The previous commit CLAIMED the widest-line rule was shared. It was not.
The daemon called `src/display_width.rs`; the GPU folded through its own
private `advance_display_col`, a second copy of the same tab-stop and
Unicode-width arithmetic. The two agreed for ordinary input, so nothing
failed --- which is precisely why the claim was worth checking and why
asserting structural protection that does not exist is the defect, not
the duplication itself.
`pmacs_protocol::columns` now owns the rule, for the same reason
`scroll::follow_left` lives there: the protocol crate is the one place
both frontends already depend on. `advance_char`, `line_columns` and
`widest_line_columns` live there with their own rows; `display_width`
and the GPU both delegate.
The sharing is now demonstrated rather than described. Mutating the tab
stop inside `pmacs_protocol::columns` breaks the GPU's
`minimap_columns_match_code_tab_and_unicode_widths` --- a row that used
to run entirely through the private copy and could not have noticed.
Also restores `r4_p1_a_chrome_press_neither_arms_nor_moves_point`'s
opening line, "P1 --- a press on the band's MODE LINE begins nothing",
which my insertion had left attached to the B2 test. The attribute came
back last round; the first paragraph did not.
`PKind::ScrollLeft | PKind::ScrollRight` were CLAIMED AND DROPPED in the
panel replay, with a comment assigning the axis to Stage 1b. That is the
"frontend emits, receiver discards" shape the panel-replay lane was
opened to fix, inherited for the horizontal axis. This closes it.
`scroll_window_columns` moves the side window's `view_left` by B7's
bound, stated exactly: `0 ..= widest - viewport`, saturating at zero, so
the final display column stays visible --- clamping at the widest line's
full width would let the origin pass every glyph and blank the viewport.
Wrap pins the origin to zero, matching `horizontal_follow`. It returns
whether the origin actually moved, which is lifetime clause 2's
"effective move".
The widest-line rule is SHARED. `display_width::widest_line_columns`
lives beside the module's other column helpers and both frontends use
it, for the same reason `scroll::follow_left` is shared: two frontends
that compute the right bound differently disagree about where the
document ends.
B2's row asserts the EFFECT --- `view_left` before and after --- not an
emission, and it carries the discriminating setup the bound requires: a
panel whose content fits has a maximum origin of zero, so the move is
absorbed by the clamp and a dropped event reads identical to correct
behaviour. The fixture gets a line wider than the viewport. Mutation:
restore the claimed-and-dropped arm, and the row fires.
Two mistakes of mine in this commit's history, both caught before it:
- I reverted a mutation with `git checkout -- src/editor.rs` on a file
holding UNCOMMITTED work, and destroyed the whole B2 implementation.
Re-applied, and the mutation check redone against a file snapshot ---
the discipline I had used earlier in the CRDT lane and dropped here.
- Inserting the new test above an existing one STOLE ITS `#[test]` and
its doc comment, so `r4_p1_a_chrome_press_neither_arms_nor_moves_point`
silently stopped being a test. Clippy's "never used" caught it. Both
are restored, and the suite count confirms 1994 tests rather than
1993.
Three implementation blockers and one evidence-labelling defect, all
from review.
THE WIRE TARGETS MULTIPLIED ONE NOTCH TWICE. `apply_wheel` banked in
LINES (notch x 3), then emitted one event per banked unit, and the
receiver applied its own SCROLL_LINES = 3 to each. So LineDelta(0, 1)
moved a panel or terminal NINE lines while the document moved three ---
and it broke the "exactly one viewport effect" witness this slice owes
before it was written. The accumulator now banks in NOTCHES, the unit
that survives the wire, and the three-line/column step is applied
exactly once at the point of effect. Pixel deltas divide by a notch's
pixel height rather than a line's.
THE MINIMAP SCROLLED THE DOCUMENT SIDEWAYS. §2a rules the minimap's
horizontal axis inert; the shared local arm was passing its banked x to
`scroll_by_columns`. It keeps its own vertical bank (B6) and no longer
moves the document horizontally.
B3's UPPER BOUND SAW ONLY THE VISIBLE SLICE. `widest_display_columns`
scanned `self.buffer.lines`, which `rebuild_code_slice` populates from
the visible window plus overscan, so every off-screen line was excluded:
horizontal scrolling clamped prematurely and the bound moved as the view
scrolled vertically. It now reads `current_text` --- the whole document
--- through a display-column rule shared with the minimap rather than a
third copy. Cost is O(document) on the wheel path, which is a real risk
against this project's wall-clock budgets and is recorded on the
function rather than pre-optimised: a cache needs an invalidation key,
and the wrong key is a worse defect than a measurable scan.
AND THE R-NAMES WERE WRONG. The rows I called R4 and R5 test
document/chrome sharing and minimap independence; the framing's R4 and
R5 are the two BUFFER-REPLACEMENT resets. The row I called R1 is basic
accumulation; the real R1 is cross-axis. Renamed, and R1's body now
asserts what R1 says --- a sub-tick horizontal followed by a sub-tick
vertical over the same surface reaches no tick on either axis.
The resets themselves are now implemented, on the buffer-replacement
path beside `code_scroll_left`, as two separate clears so that omitting
one is individually visible. Their witnesses --- an actual replacement
through the harness --- are still owed and are labelled as such.
The producer 1b owes. `apply_wheel` used to round to whole lines and
return on zero BEFORE consulting the pointer, so every sub-tick delta
bound for the panel or the terminal was discarded by a decision taken
upstream of routing. §2a CORRECTION 5 measured that ordering; this
inverts it.
The pipeline is now: classify the target, bank the fractional delta
against THAT target's accumulator, route only the whole ticks that fall
out. `WheelTarget` exists because `PointerSurface` cannot name what B1
needs --- it resolves panel geometry only and collapses the document,
the terminal, the minimap and the chrome into one `Elsewhere`, three of
which B1 and B6 must keep apart.
Residual owners follow §2a's enumeration exactly: per panel, per
terminal, the minimap's own, the document's --- and chrome shares the
document's deliberately, so a gesture that strays onto the gutter does
not lose its banked motion. Panel divider and background bank NOWHERE
and clear the panel banks: a residual they could share with a cell would
let motion over an inert strip complete a tick the moment the pointer
entered a live one, which is the surface-switch jump B1 exists to
forbid.
Nine rows, including R1-R5's identity discriminators and §2a's required
crossing witness. `trunc` rather than `round`, so a half-tick that was
never delivered is not spent.
Two things this commit does not do, both recorded rather than stubbed:
- IDENTITY'S SECOND HALF --- disposal --- is owed. A residual keyed to a
surface that goes away must go with it, and this frontend does not
currently track "that buffer is gone". A helper nothing calls would
read as a contract met, so the method is absent and the gap is
documented on the type.
- `scroll_by_columns` and the manual-authority latch land here as B3/B7
and Q#S1-11's foundation, but their witnesses (L1-L8) do not. They
come with the horizontal wire path.
One behaviour regression caught by an existing row and fixed: a wheel
before the first cursor motion has no pointer position, and the first
draft dropped it. It targets the document, as it did before 1b.
Step 3 asked only that "a wheel" move the panel. A whole tick passes
straight through #243's existing vertical receiver even if B1's new
fractional accumulator discards every sub-tick it is given --- so the
witness was satisfiable with the exact mechanism it exists to protect
completely broken. The justification for owing it at all is that B1's
producer is NEW, and a witness that never exercises the producer proves
nothing about it.
It now requires fractional input end to end, per axis: a first
individually sub-threshold delta produces NO viewport effect, and
further same-panel sub-threshold deltas, once accumulated past a tick,
produce EXACTLY ONE. Not zero, not two. §1b's panel-replay paragraph
carries the same requirement so a reader who starts from the contract
table sees it.
Also: the v25 note still said the mapping slice "lands" ahead of 1e
"because panel-pointer replay blocks 1b". Both landed --- #242 and
#243. It is historical now: v25 landed ahead BECAUSE replay then
blocked 1b, and the version numbering is what survives.
The verification base still said `72da24a` in three live places: the
§2 header's "Verification base" line, §2a's own heading, and §1b's
blockquote. All three now name `0ec13b3` while preserving `72da24a` as
the original measurement.
The live panel-replay section still contradicted the discharge --- it
said 1b "does not ship before" the prerequisite, "depends on that
lane", that vertical panel scrolling is "equally dead today", and it
repeated the three-step future ordering. The ruling and its two reasons
are now marked as made-and-carried-out; steps 1 and 2 are struck as
done; step 3 stands.
And the conceptual point behind that, which I had wrong: #243
discharges the PREREQUISITE completely within its scope and supplies
the vertical RECEIVER path. It discharges NONE of B1's witness, on
either axis, because B1's producer --- a per-surface fractional
accumulator --- is new and no #243 test exercises it. A vertical panel
wheel routed through a new accumulator can drop sub-ticks or carry
residue across a surface switch. Implementation already present is not
evidence already owed, and saying "#243 discharges the vertical half of
the witness" conflated the two.
The sweep also verified RANGE, not IDENTITY, which is the weaker check.
Six citations landed in the right file and the wrong construct: B4's
`UnusedButton`/`route_pointer`, misfiled against editor.rs when they are
main.rs; the panel paint call; `OwnCursor`'s type doc against its field;
the GPU wrap branch against the `horizontal_follow` call; and
`scroll_top`'s zeroing, one line before the residual's. Every live
citation is now checked against the construct its prose names.
Revision 18's §2a was measured at `72da24a`, now 167 commits back, with
#242 and #243 landing on exactly the pointer paths 1b builds on. Every
1b anchor is stale again, which §2a's own history predicted.
All four load-bearing claims were re-verified and all four SURVIVE.
Quantization still happens BEFORE routing (:3254 quantize, :3262 return
on zero, :3270 panel routing) --- checked first, because an inversion
would have changed B1's design. `Elsewhere` still collapses four wheel
targets. The panel branch still tests `PanelCell` only, so B6's premise
holds. `code_scroll_residual` is still the caret-follow residual, not a
wheel accumulator.
Three base facts were wrong:
main.rs is 22,502 lines, not 21,435
PROTOCOL_VERSION is 25, not 24 --- #242 took v25
the panel-replay premise is stale
That third one matters most. §2a said a panel wheel "does nothing today,
on either axis". #243 landed the replay and it is in this base: the
dispatcher is split into `classify_panel_pointer` and
`apply_panel_pointer`, and the vertical axis replays for real. THE
PREREQUISITE IS DISCHARGED. What does not lapse is the witness --- 1b
still owes an end-to-end panel-wheel EFFECT witness on both axes, and
#243 discharges only the vertical half.
Citation policy: 45 live citations were re-measured and renumbered; a
citation that records what an EARLIER revision cited is left exactly as
written, and its block is labelled "citations are HISTORICAL
(72da24a)". Renumbering those would falsify sentences whose whole point
is that an earlier citation was wrong --- revision 15's test-fixture
finding is the clearest case.
Verified after the sweep: 79 §2a citations, zero out of range, and the
seventeen load-bearing anchors each resolve to the construct they name.
No ruling changes. Re-measurement, three corrections, one discharge.
`docs/gui-stage1-input-framing.md` was advanced independently by two
branches and both called their change "revision 13".
gui-stage1b-pointer-scroll 13->18 adds §2a, 503 lines: six
corrections, B1's wheel-target
enumeration, Q#S1-11 ruled (B),
the lifetime contract with L1-L8
--- APPROVED at 18
panel-replay lane (1fc3af6) its 13 the enriched B1/B2/B3 rows and
the "Panel-replay consequence"
paragraph --- approved by merge
in #243, status line never updated
Both are kept in full, verified rather than assumed: the enriched rows,
the panel-replay paragraph, `wrap pins horizontal origin to zero`, §2a,
the six-target enumeration and L1-L8 are all present after the rebase.
Nothing is renumbered retroactively --- rewriting those numbers would
falsify sentences like revision 15's "three defects in 14", and the
duplicate is recorded instead.
They turn out to be complementary. The panel-replay row names three
residual owners; §2a's model is a superset that adds the TERMINAL and
rules that chrome shares the DOCUMENT's residual. Where they do
disagree, §2a wins, as §1b's own blockquote already said.
Rebased onto 0ec13b3. The branch's stale copies of active-work.md and
ci-red-signatures.md were resolved toward main, which is 167 commits
newer; both are now byte-identical to main.
Records the ruling. SS2a is the 1b ground truth and its contents are
settled: Q#S1-11 (B), the five-clause lifetime contract with L1-L8,
B1's six-target enumeration with R1-R5, and B3's exact saturated bound.
Also records what the six rounds cost, because the pattern is the
point: 13 through 18 each found a witness that would have PASSED
against a broken implementation --- emission-only panel rows against a
receiver that discards, a GPU preservation row driven by a paint that
never calls the follow, lifetime rows whose setup left held and
released authority indistinguishable, mutation dependents predicted
rather than derived, and a residual-identity story that stopped at the
surfaces which get destroyed and missed the two that outlive the
buffer. None was a design disagreement; the rulings from 14 and 15
survived untouched. That is why the R- and L-rows specify their DRIVERS
and SETUPS and not only their assertions.
The framing no longer gates this lane. The panel-replay prerequisite
does: it merges first, 1b rebases onto that merge commit, and 1b's base
moves off `72da24a`.
Gates: all nine green under `env -u TMPDIR`, log 20260814T111537Z.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016bqGA6s9tTUFzYpbeW3tai
Answers review of revision 17. Still framing only; no code.
**IDENTITY HAS A SECOND HALF, AND R2/R3 CANNOT REACH IT.** A panel or
terminal residual is keyed to a surface that GOES AWAY, so replacement
is naturally observable. The document's and the minimap's do not work
that way: they live in the long-lived GPU `State` and OUTLIVE the
buffer. Their reset is a manual list --- buffer replacement
(`pmacs-gpu/src/main.rs:5922` onward) zeroes `scroll_top`,
`code_scroll_residual` and `code_scroll_left` one explicit line at a
time, each with its own comment. A new wheel residual will not reset
itself, and nothing fails if it is forgotten except a user's next
document scrolling from someone else's offset.
R4 document A -> document B, covering the document residual AND the
chrome residual it shares; successor starts from zero.
Mutation: omit the document residual from the replacement reset.
R5 minimap across A -> B; its residual is independent (B6) so it
needs its own reset and its own leg.
Mutation: omit the minimap residual from the replacement reset.
The mutations are deliberately separate --- one reset omitted at a time
--- because a single "forgot to reset" mutation would bite both legs
and prove neither field individually covered. `code_scroll_left` had
precisely this defect once, which is why it now carries a comment
explaining that a switch would otherwise inherit the previous
document's leftward viewport.
**AND TWO OVERCLAIMS IN THE SIGNATURE ROWS.** I wrote that both steps
ran "the same executable". They ran the same TARGET but different
ARTIFACTS: `pmacs-9d4963151c6f3151` at 211,777,472 bytes and
`pmacs-7624ed4bed0b6617` at 214,462,056, and `cmp` reports differing
bytes. What is supported is the same libtest target, its 1,940-test
population, and default intra-binary threading --- which is all the
concurrency conclusion ever needed, so that conclusion is unchanged.
"Cargo runs test binaries sequentially" was also stated as a universal.
It is narrowed to what was observed: under cargo 1.95.0, this sweep ran
its 119 binaries sequentially. The rows no longer generalize to cargo
versions or invocations they did not measure.
Gates: all nine green under `env -u TMPDIR`, log 20260814T110707Z.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016bqGA6s9tTUFzYpbeW3tai
Answers review of revision 16. Still framing only; no code.
**"PER AXIS AND SURFACE" HAS TWO NOUNS AND 16 WITNESSED ONE.** The
background-to-cell crossing row proves a CATEGORY change and nothing
else, so an implementation could still share a residual between the two
axes, or between two panels, or between two terminals, and pass every
row written. Three discriminators added:
R1 cross-axis --- a sub-tick horizontal then a sub-tick vertical over
ONE surface must reach no tick on either axis.
Mutation: one residual per surface rather than per (surface,axis).
R2 panel A -> panel B replacement; motion over B starts from zero.
Mutation: key the residual by "the panel", not panel identity.
R3 terminal A -> terminal B, likewise.
R2 and R3 also pin discard-on-disappearance: a residual banked against
a surface that no longer exists must not be inherited by its successor.
Acceptance 49's epoch machinery is the existing precedent.
The end-to-end panel effect witness now runs BOTH AXES. "A wheel moves
the viewport" leaves the horizontal half untested exactly where the
emit-and-discard defect lived --- the axis with no handler at all.
**TWO MUTATION DEPENDENCIES WERE PREDICTIONS, NOT DERIVATIONS.**
Suppressing cursor-change release need not fail L8: clause 5 gives
wrap and buffer replacement their own explicit clear, which does not
run through the release path, so the latch is cleared and L8 passes.
Making the horizontal wheel write point need not fail L4: L4 asserts
the horizontal origin survives a VERTICAL wheel, and a spurious write
on the horizontal path does not change what the vertical path does.
Scoped to L3 and L5. Additional bites are now recorded only after a
mutation run shows them, with the run named.
**AND U10's CONCURRENCY CLAIM WAS FALSE.** I wrote that U10 ran U9's
comparison "in the opposite direction" --- red at low concurrency,
green at high. It did not. Cargo runs test binaries SEQUENTIALLY: the
sweep log shows strict `Running` -> `test result` -> `Running`
alternation across 119 binaries, the failing test is in the FIRST one,
and it is the same 1,940-test libtest executable `03-lib` ran with the
same default intra-binary threading. There was no concurrency
difference between the two steps to compare, so no experiment reversed
anything.
U9's candidate is withdrawn on that ground --- its premise was false,
which is a stronger and cheaper retirement than an experimental one,
and it never needed a run. What U10 establishes is what it always
established: another intermittent occurrence with an in-run control, on
a documentation-only diff. Synthetic load against the `--lib` binary
alone remains the useful experiment and remains unrun. Both rows
corrected in place, because the wrong version is what a later reader
would otherwise reconstruct from the two step names.
Gates: all nine green under `env -u TMPDIR`, log 20260814T084344Z.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016bqGA6s9tTUFzYpbeW3tai
Answers review of revision 15. Still framing only; no code. Three of
the four changes correct 15's own reasoning.
**THE PANEL PREREQUISITE IS NOW IN THE TOPOLOGY, NOT A FOOTNOTE.** The
defect is exactly "frontend emits, receiver discards", so 15's
emission-only panel witnesses would have reproduced the blind spot that
let it sit undetected --- passing rows meaning nothing about whether a
panel wheel scrolls. SS3 now reads `1a` -> panel-replay lane -> `1b`:
the lane MERGES FIRST, 1b REBASES onto its merge commit (its base moves
off `72da24a`), and 1b carries an END-TO-END panel-wheel EFFECT
witness.
15's rationale "it is not input work" was wrong --- replay covers
terminal mouse reporting and click-to-focus, which is input work. The
accurate reason is breadth and ownership: acceptance 48
(`docs/bottom-panel-framing.md:1719`) has `PanelPointer` driving
listview row selection, panel selection, terminal mouse reporting and
click-to-focus. A wheel is one gesture in that set.
**DIVIDER AND BACKGROUND MUST BANK NOTHING.** 15 gave them the panel
cell's residual, which manufactures the very surface-switch jump B1
forbids: motion banked over an inert target, then spent the instant the
pointer enters a cell, so a gesture that scrolled nothing completes a
tick on arrival. They discard and reset both axes; any state kept there
must be incapable of combining with cell input. A crossing witness
pins it --- background partial plus cell partial must not reach a tick
--- and its mutation is precisely 15's error.
**THE DISCRIMINATING SETUP BELONGS TO EVERY LIFETIME ROW.** 15 stated
it for L3 alone. With the cursor inside the manually scrolled viewport,
`follow_left` returns that same origin, so held authority and released
authority produce IDENTICAL state and the row passes either way. L1,
L2, L4, L6, L7 and L8 now share L3's cursor-outside setup. L2 becomes a
HEIGHT-ONLY GPU resize, so it invokes the real follow without moving
the clamp. L7 splits: 7a widens the viewport, 7b SHRINKS THE CONTENT
--- clause 3 promises re-clamping on geometry AND content, and widening
witnesses only the geometry half.
**THE MUTATION TABLE NOW OBEYS SS6.** 15's heading promised "own rows
and no others"; SS6 of this same document says that promise is false
and cannot be made true, and requires instead that a mutation bite its
named rows with dependents NAMED. Rewritten that way, with the
legitimate dependents listed per mutation.
Ledger: checkpoint was still "revision 14", and described the snap-back
as next-paint without the TUI/GPU split that 15 established. Both
corrected, and the prerequisite recorded as a blocker with the rebase.
Also records CI-red signature **U10**. The first gate run of this tree
red on `composition_overhead_under_ten_percent` in `03-lib` at 1.343x,
and the SAME RUN's sweep passed it, on a documentation-only diff. Worth
a row beyond the usual: U9 asked whether sweep test-binary concurrency
was the discriminator, and this occurrence runs that comparison in the
opposite direction and gets the opposite result --- red at low
concurrency, green at high. That retires the candidate without
supplying a cause; two occurrences pointing opposite ways across one
variable means the variable is not the discriminator, nothing more.
U9's synthetic-load control is still the useful experiment and is still
unrun.
Gates: all nine green under `env -u TMPDIR`, log 20260814T082533Z.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016bqGA6s9tTUFzYpbeW3tai
Answers review of revision 14. Still framing only; no code. Revision
15 DOES change two B-row contracts, B1 and B3; 14 claimed none changed
and that was false.
**I CITED A TEST FIXTURE AS A PRODUCTION HANDLER.** Revision 14 named
`src/daemon.rs:6683` as the panel's `ScrollUp` arm. It is inside
`#[cfg(test)] mod tests`, opened at `:3740`. The wrong citation hid a
real defect: the production path,
`dispatch_semantic_panel_pointer` (`src/editor.rs:2674`), validates the
coord, resolves the side window, focuses when the gesture activates,
and returns WITHOUT REPLAYING ANYTHING. Its own doc says replay is
parent acceptance 48 and lands in Stage 2B-3. So a panel wheel is dead
today on BOTH axes --- a pre-existing violation of an already-ruled
contract, which is why panel inertness was never an available answer.
Ruled: 1b does NOT absorb the replay. 1b owns the frontend half ---
per-panel residual, both axes emitted --- and replay is repaired in a
prerequisite lane. It is already scoped to 2B-3, it is not input work
(selection, listview rows, child SGR reporting, and it needs the GPU
band), and the defect predates this slice. 1b's panel rows therefore
witness EMISSION ONLY and must say so rather than implying a scroll the
user cannot yet see.
**B1 IS NOW RULED, NOT HALF-RULED.** Revision 14 left two cells reading
"must be ruled", which is a question wearing a table's clothes. The
terminal answer was derivable all along: the SGR encoder already
carries `ScrollLeft` as 66 and `ScrollRight` as 67
(`src/terminal/input.rs:126`), and its guard returns `None` unless
reporting is on --- so a non-reporting terminal is horizontally inert,
matching the TUI, and no new arm is needed. Divider and background now
CONSUME both axes, because falling through to the document contradicts
`PanelBackground`'s own doc that the band owns the pixel. Chrome shares
the document's scrolling and residual, stated so it is a choice rather
than a leak.
**THE GPU PRESERVATION WITNESS WAS VACUOUS.** "The next paint" is
TUI-only. The GPU's `render()` (`:9881`) goes straight to
`render_to_view` and never calls `horizontal_follow`; the follow
reaches it only via `ensure_caret_painted`, whose callers are cursor
paths and geometry paths (`resize`, `apply_font_facts`,
`reflow_dynamic_code_geometry`). A GPU wheel-then-paint row would have
stayed green with the overwrite mutation restored. Preservation is now
specified per frontend: TUI drives a real paint, GPU drives a real
same-cursor geometry re-follow.
Release must move the cursor OUTSIDE the manual viewport. Inside it
`follow_left` returns the same origin, so the row would pass whether or
not release happened.
**FIVE CLAUSES HAD THREE WITNESS SHAPES; THEY NOW HAVE EIGHT.** 14 left
unconstrained: point and selection staying put, clamp-absorbed motion
not arming authority, geometry/content changes re-clamping while
PRESERVING authority, and wrap/buffer replacement clearing the LATCH
rather than merely zeroing the origin. The existing wrap-origin rows
cannot see a stale latch surviving wrap then truncate. Six mutations,
each failing its own rows.
Clause 3's direction was backwards: a WIDER viewport lowers the maximum
origin, so widening re-clamps downward. 14 said "narrower".
Ledger: three false claims removed. The 1b entry arrived in the SECOND
commit, not the first --- recorded rather than quietly fixed, because a
lane claiming compliance it did not achieve is what the #171/#215
correction exists to catch. The entry no longer says all six targets
have horizontal answers. And the arc block said five slices remain, 1a
is next, and v23; it is four, 1b, and v24.
Gates: all nine green under `env -u TMPDIR`, log 20260814T075217Z.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016bqGA6s9tTUFzYpbeW3tai
Answers review of revision 13. Still framing only; no code.
**Q#S1-11 IS RULED (B), VIEWPORT ONLY.** (A) is not viable in 1b and
the vertical precedent does not reach it. `scroll_window` can carry
point because it is TUI-side, where the editor owns the cursor.
`OwnCursor` is "pmacs-gpu's own cursor position, MIRRORED from
`CursorByte`" --- a read-only reflection --- and the only wire operation
that positions it, `dispatch_pointer`, sets `active_frontend`, calls
`break_command_chain`, and by its own comment moves point or changes
selection on every kind. Carrying point from a wheel therefore needs a
NEW wire operation, which contradicts 1b's non-protocol scope. (A)
would not even be self-consistent: GPU vertical scrolling already does
not carry point.
**And "do not carry" alone is not a ruling** --- it leaves the origin's
lifetime undefined, which is the part that decides whether the feature
works. Five clauses, all of them the ruling: viewport only; an
EFFECTIVE move arms authority; repaint, same-cursor follow, resize and
vertical wheel preserve it while geometry and content changes CLAMP
rather than release; a genuine cursor-position change releases it; wrap
and buffer replacement clear it and pin zero.
**THE HAZARD LANDS ON THE NEXT PAINT, NOT THE NEXT CARET EVENT.**
Revision 13 said caret event and understated the exposure:
`horizontal_follow` is the FIRST act of `prepare_window_cursor_visible`,
which `paint_frame` runs every frame. The origin is overwritten by a
redraw with no input at all. Witnesses must therefore drive real call
sites on both frontends --- a helper unit test cannot see a follow that
runs inside `paint_frame` --- and a TUI cross-axis row is required
because vertical wheel DOES carry point there. Both mutations named:
follow ignores authority, and authority never releases.
**B1'S "SURFACE" IS NOW ENUMERATED.** Two facts made the gap real:
quantization and the zero return happen at `:3074`, BEFORE the panel
(`:3090`) and terminal (`:3112`) branches, so a sub-tick delta is
discarded before anything knows where it was going; and
`PointerSurface::Elsewhere` conflates document, terminal, minimap and
chrome, so the existing classifier cannot name what B1 needs. Six
targets are tabulated with a residual owner and a horizontal answer
each. The horizontal gap is NOT a wire gap --- `MouseKind::ScrollLeft`
and `ScrollRight` already exist and round-trip --- it is a missing
handler, so each target must be ruled emit-and-handle or explicitly
inert. Without the table, one global accumulator passes every
per-surface row that tests one surface.
Recorded while measuring: a wheel over the panel divider or the band's
background scrolls the DOCUMENT today, though the enum says the band
owns the pixel.
**B3 TAKES B7'S EXACT BOUND.** "Content bounds" was vaguer than B7's
saturated widest-line-minus-viewport rule, for the same bound on the
same rule, and B7 is exact precisely because the loose version blanks
the viewport. Stated in the GPU's column grid, since
`horizontal_follow` already reckons there and re-multiplies to snap the
offset --- a pixel clamp would break the snap the shared rule depends
on. Narrow-buffer and final-column-visible rows added; a clamp at full
content width must fail.
Ledger: the minimal 1b recovery/checkpoint entry, plus three lane
headers that were lying --- #240 and #239 were still marked OPEN, and
the arc header still said 1a was next. The #239/#240 absorption stays
deferred and is a separate lane.
Gates: all nine green under `env -u TMPDIR`, log 20260813T211645Z.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016bqGA6s9tTUFzYpbeW3tai
Adds SS2a, measured at `72da24a`. No B-row contract changes. Awaiting
approval; nothing is implemented on this branch yet.
Every 1b anchor was stale, which was the expected result --- 1-pre's
router extraction moved the wheel path wholesale and `main.rs` is now
21,435 lines. `main.rs:2061` holds `PanelCell`, `:3337` an
`attach_client` line, `:3373` a bare brace; `dispatch_mouse` is
`editor.rs:3207`, not `:3052`. Those are renumbered in a table.
Three findings are not renumbering.
**THREE ROWS SAY "NOTHING EXISTS" WHERE SOMETHING DOES.** B3's "no
horizontal scroll to clamp" --- the GPU has `code_scroll_left` and its
own `horizontal_follow`; what is missing is a WHEEL-driven one, so B3
adds a second writer to an existing field. B5's "no I-beam" is true,
but `apply_panel_cursor_icon` already sets `CursorIcon::Default`
everywhere that is not the divider, so an I-beam written as its own
site would be clobbered by that else branch. B4's "no middle-click
path" --- 1-pre built the landing site and named this very row in its
doc comment. Each contract is unaffected; each implementation shape is
not.
**AND B7 RE-OPENS A QUESTION ANOTHER FRAMING DEFERRED.** This is the
one that needs a ruling. `horizontal_follow` documents the premise
"there are no explicit scroll commands, so every viewport move
originates here, and Q#HS4's snap-back hazard cannot arise". B7 IS an
explicit horizontal viewport move, and the horizontal-scroll framing
says in terms that such commands "are what re-opens Q#HS4".
The hazard is not hypothetical --- it is why `scroll_window` carries the
cursor with a VERTICAL wheel scroll: without it the auto-scroll pass
snaps the origin back and the wheel "would feel stuck after one notch".
A wheel-driven `view_left` that does not carry the cursor gets the
identical bug one axis over, on BOTH frontends, since the GPU's
`horizontal_follow` has the same shape.
B7's contract does not mention the cursor, so its mutations cannot
detect this: a clamp row and a wrap row both pass against a viewport
that snaps back on the next caret event. Q#S1-11 is raised OPEN with
two candidate answers, and either way B7 needs a witness the table
lacks --- scroll sideways, trigger a caret-follow, assert the ruled
behaviour.
Also worth having: B7's wrap clause is already implemented for the
caret-follow path, and B1/B2 share one witness, the four lines in
`apply_wheel` that round to whole lines and discard both the remainder
and the x delta. `code_scroll_residual` is NOT a wheel accumulator ---
it is the caret-follow pixel residual --- and reusing it would be a
defect.
Gates: all nine green under `env -u TMPDIR`, log 20260813T202304Z.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016bqGA6s9tTUFzYpbeW3tai
The post-merge gate of the docs-only absorption commit redded at
`07-sweep` with U16's exact signature. Same selector, same panic site,
both required fragments.
It is the first occurrence on `main`, which removes the last attribution
question this row could have had --- the first two were on a branch
whose diff touched nothing under `src/packages/`, and this one has no
observing branch at all.
Three occurrences in about eleven hours, against eight consecutive green
`--lib` runs earlier the same morning. That is the closest this row has
to a rate and it is still not a measurement, because nobody has counted
runs and failures over a fixed window. Isolated reruns green three
times, which per the rerun rule establishes intermittence only.
The structural fix belongs to `file_io`: a version-suffixed child
inherits a cwd that a `TempDir` then deletes, and no care inside the
mutating test can close a process-wide window.
Merged at the approved head `093d677` via `--match-head-commit`, 14/14
CI green and the eight-stage local gate green on that exact commit.
The lane block keeps its full review history rather than collapsing to
a one-line "merged": ten rounds, four on the framing and six on the
implementation, and almost every finding in the later six was against a
RECORD rather than against code.
The heading said "a terminal bell never arrives within a 5s poll" while
the body two paragraphs down withdraws exactly that: the evidence shows
the bell was not OBSERVED within five seconds, not that it never came. A
title is the part most readers keep, so it was the worse place to leave
it. Retitled to match.
U16 said its reds and greens were "across two days". Every run the row
cites --- both reds and every green --- is 2026-08-31, which the
sentences immediately above it already said twice ("again the same day",
"returned within the day"). Corrected in both files.
U19 said "no rerun was performed on this selector". The very next gate
run was one: it passed in `03-lib`, `04-lib-crdt` and the exact
`07-sweep` context it failed in, at ea786a2. So did U16's selector,
after its second occurrence. Both rows now record those passes and
classify as intermittent. Writing "no rerun was performed" in the same
commit whose gate reran it is the kind of claim this file exists to
catch.
U19 also overstated three things. The evidence shows no bell was
OBSERVED within five seconds --- not that one "never comes", and not
that scheduling cannot explain it. "Three orders of magnitude of slack"
does not hold against the 200ms budget it cited: 5s is 5000x of 1ms but
only 25x of 200ms, so the distinction from the budget family is one of
degree. And adding an elapsed value later cannot make a future margin
comparable with THIS unmeasured one --- that margin is gone for good; it
only makes future failures comparable with each other.
R7 kept a sentence reconstructed before the renumbering: its "prior five
spread across lanes and months" were eight, and not evenly spread ---
three of them fall on one branch on 2026-08-15. The line census skipped
occurrence five, whose block never captured a line; it is now marked
unrecorded rather than guessed or omitted.
U16's introduction still said it was "worth more than its one
occurrence" while its status said second.
The gate verifying the R7 renumbering redded twice in `07-sweep`.
`cache_survives_across_fetcher_instances` is **U16's second
occurrence** --- same selector, same panic site, both required
fragments. It is the first time that row has reproduced, and it settles
an earlier withdrawal in the right direction: claiming "the window is
narrow" from eight green runs was wrong, and the failure came back
within the day. The child-inheritance chain stays a candidate; this
occurrence demonstrates it no more than the first did.
`terminal_bell_baseline_suppresses_history_and_delivers_each_new_bell_once`
is new, recorded as U19. It is a deadline but not the budget family's
kind: those assert work finishes in 1ms or 200ms, while this asserts an
event arrives at all inside FIVE SECONDS. Folding it into that family
would blur the one distinction those rows have.
Like R1, its assertion has `Instant::now()` in hand at the panic and
reports none of it, so the margin is unrecoverable and a future
occurrence will not be comparable to this one. That is the second place
in this codebase where the same omission costs the same thing.
`docs/active-work.md` recorded two full-fragment R7 occurrences from
2026-08-15 (logs 20260815T095532Z and T100719Z, `attach.rs:1728`) under
a heading saying they were "owed to the registry by whichever branch
merges second". Both branches merged. Nothing carried them across, and
they sat there for sixteen days, so R7's count read two low even after
yesterday's renumbering.
With those absorbed and the duplicate "fourth" fixed, the sequence is:
August 29 = ninth and tenth, August 30 = eleventh, August 31 = twelfth.
The parse-budget lane block, which still said sixth and seventh, is
updated too.
The deferral itself was reasonable --- this file has been bitten by two
branches inventing the same row id --- but not discharging it was not.
The lesson recorded is narrower than "absorb faster": an entry parked
under "owed to the registry" needs an owner named in the same sentence,
or it belongs to nobody.
The summary cell's line-specific claim was also incomplete: occurrences
one through FOUR report `attach.rs:1680`, not the first three.
And U18 over-corrected. `GONOSUMDB` is a real Go variable ---
`go help environment` documents `GOPRIVATE, GONOPROXY, GONOSUMDB` as
module prefixes "that should not be compared against the checksum
database", which is exactly the step that failed. It is technically
applicable; whether the authentication tradeoff is acceptable is a
different question. Discarding a real knob while correcting an invented
one is its own error and is recorded as one.
R7 carried TWO blocks numbered "fourth" --- D3 on 2026-08-11 and TMPDIR
isolation on 2026-08-13 --- so every later ordinal was one low. The new
red is R7's TENTH, not its ninth. Renumbered by date, with the duplicate
recorded in the status cell rather than silently fixed. The summary cell
said "three occurrences" and now states the total, keeping the
`attach.rs:1680` fact as the line-specific claim it always was.
U9's selector was named wrong in yesterday's correction. The row names
the CANONICAL pty test (`src/process.rs:3967`), not
`raw_mode_disables_kernel_echo` (`:3945`) --- and U9's own "relation to
U2" cell turns on exactly that distinction, so getting it backwards
would have undercut the row the paragraph was correcting.
The arithmetic was still wrong in two places. There are 121 result lines
and the doc-test groups are numbers 120 and 121, not 121 and 122; and
U9's table cell still asserted the strict 119-to-121 alternation that
the paragraph below it retracts.
U18 listed three outage options and all three were wrong. `GONOSUMCHECK`
is not a Go environment variable --- that sentence invented it.
`GOFLAGS=-mod=mod` selects module update mode and does not bypass
checksum-database authentication. And a version-suffixed `go install`
ignores vendor directories, so "a vendored gopls" needs a different
installation path. No replacement knob is named, because none was
verified.
The ledger called uncontrolled foreign load "the same evidence U9's
synthetic-load control was meant to produce" and said "U9 stays owed",
both of which contradict the correction below them. And its lane heading
said four registry rows moved while listing eight.
The gate verifying the previous commit redded at `gpu` with all three of
R7's required fragments, same selector and same line. Other seven stages
green; the observing commit is documentation only.
It adds a count and nothing else, which is the honest description. The
eighth occurrence's method note says the remaining candidates must be
varied inside the gate, one per run, and that is not this lane's work.
The loadavg reading is recorded as a condition, not a cause --- R7 is
not a budget row.
Three corrections to yesterday's correction, and one new row.
The serial-binary measurement was stated as "119 Running and 121 result
lines alternate strictly", which cannot be strict --- the count itself
gave it away. Precisely: 119 ordinary targets each report before the
next starts, and the two extra result lines belong to `Doc-tests pmacs`
and `Doc-tests pmacs_protocol`, which cargo labels differently and runs
last.
The replacement premise did not describe U9 either. Both U9 selectors
live in the ROOT LIB TARGET --- `m6_1_pty_raw_mode_disables_kernel_echo`
(src/process.rs:3945) and `composition_overhead_under_ten_percent`
(src/editor.rs:9717) --- and the sweep runs that target first, finishing
it in about 12 seconds. The sweep's later minutes cannot reach them.
What survives: the sweep re-runs the lib target late in the overall gate
invocation, under unmeasured machine state.
Two stale references to U9's void control are corrected, including the
ledger's claim that a synthetic-load run would "either implicate load or
clear it". It would not: with concurrency fixed at 1 there is no second
arm, so a red shows load is sufficient and a green shows nothing.
Non-reproduction never clears anything under this file's own rerun rule.
U18 is new and a new class. `Test (ubuntu-latest / luajit)` died in
toolchain setup before any cargo command ran: `go install gopls@v0.16.2`
hit an HTTP/2 INTERNAL_ERROR from sum.golang.org while verifying
x/telemetry. Every other row here is a test that failed; this is
infrastructure the workflow depends on failing to answer, and it
presents as a red check indistinguishable from a real one.
U9's "structural difference worth testing next" claimed that
`cargo test --workspace` runs many test binaries concurrently while
`--lib` runs one, and derived its discriminating control from that:
"pin test-binary concurrency to 1". The premise is false. Cargo runs
test TARGETS serially, one executable at a time, so that concurrency is
already 1 and the control pins nothing.
Measured in this project's own gate logs rather than asserted from the
cargo book: `20260831T093655Z-857818/07-sweep.log` alternates `Running`
and `test result:` strictly --- 119 to 121 markers, ZERO cases of one
binary starting before the previous reported. The pattern is `RTRTRT`.
That falsifies a premise two rows rested on, so U12's family paragraph
is corrected too. What survives is smaller and still true: a sweep is a
long sequence of binaries, so a budget inside it runs at an arbitrary
point in a multi-minute step. The family still should not consume review
rounds --- but it now needs a control someone has to design.
U17 no longer claims `--test-threads=1` exercises U9's control. It is a
different knob: it serializes test FUNCTIONS within one executable. Its
candidate mechanism is narrowed to match --- removing sibling test
functions removes ONE source of contention, which supports neither
"fastest" nor "narrowest".
R6's block drops two overclaims: a PR run CAN show the identical red
(only the main dispatch establishes it on the merge base), and this was
not the dispatch key's first use --- #245's D2/D3 dispatched three runs
right after it merged. It is the first use for a live merge-base
control.
Five corrections, all mine.
U16 stopped at "the window exists", which misses why restoring the cwd
does not close it. `run_git` calls `run_git_inner(None, ...)`, and that
sets `current_dir` only when `cwd` is `Some` (fetcher.rs:329-330), so
the spawned git INHERITS the parent's temporary cwd. The parent then
restores its own --- which does nothing for a child that already has its
working directory --- and the TempDir drops underneath it. The restore
is not merely too early; it is irrelevant to the child.
U16 also offered a serial guard around `set_current_dir` tests as a
structural control. That does not protect an unguarded test that spawns
a child, because the child outlives the guard. The options that work are
removing the cwd mutation, running that test in a subprocess, or
serializing the whole lib-test binary.
And U16 said 8 green runs showed the window was narrow. They do not.
Non-reproduction establishes intermittence and nothing else; nothing
here has sized this candidate's window.
U17 claimed no PR run can show its failure. A PR run exercises the same
test and could fail identically; what only a main-side run establishes
is that it fails ON MAIN, with no observing branch to suspect. And its
`got ok` does not prove the supersede arrived late --- it proves the
predecessor completed successfully before cancellation took effect,
which a timely supersede whose cancellation lost the race produces
identically.
The macOS lua54 leg redded on PR #246 with
`acc28_child_input_and_the_c_c_escape_work_unchanged_in_a_panel`. It is
a full three-condition match for R6 --- selector, flavor, and BOTH
required fragments (`timed out waiting for` + `/ready`) --- 26 days
after the first occurrence.
The log was read BEFORE anything was rerun. U3 named that lesson and U8
recorded its fourth violation; this is the first time it was followed on
a macOS job at the moment it mattered, and the fragments exist because
of it.
Rather than argue from an unrelated diff, a merge-base control was
dispatched at `aae5b35` --- the first real use of the
`workflow_dispatch` key #245 landed, and exactly the case U11 motivated
it for. The macOS legs came back GREEN, so the inference the control
could have supplied is unavailable. Recorded as a null result, the way
R1's row had to record its own. What each outcome would mean was written
down before the result was seen.
The control was not otherwise clean: `Test (crdt)` failed on `main`,
which is U17. It fails the opposite way to R1 and R5 --- not a missed
deadline but a predecessor that had already completed --- and the job
runs `--test-threads=1`, the condition U9's still-unrun control names. A
red on the merge base is invisible to any PR run.
The sweep step redded on `cache_survives_across_fetcher_instances` with
`fatal: Unable to read current working directory`. Not a budget test,
and not a load story.
It is the only row in this file that arrives with a named candidate
mechanism inside the test suite. `src/file_io.rs:434` calls
`std::env::set_current_dir` --- process-global state --- inside a test
running in one of libtest's parallel threads, points it at a `TempDir`,
and lets that `TempDir` drop. Every other test in the binary shares that
cwd for the window, and after the drop it is a deleted directory, which
is exactly what git reported.
Recorded as a candidate with a citation, not a demonstrated chain: 8
full parallel `--lib` runs did not reproduce it, which says the window is
narrow rather than absent. The row names the two controls that would
settle it and runs neither --- the structural fix is `file_io`'s, not a
CRDT invariant lane's.
Two precision errors, both mine.
U14 said "four selectors in three unrelated subsystems". They are four:
the async runtime, the optimistic-echo orchestrator, editor composition,
and the LSP dispatch seam. U6's own row treats its two selectors as
unrelated subsystems, so the `04-lib-crdt` pair is two of the four here,
not one. The `what is NOT` row said three as well.
U15 claimed to be the load number "U6 and U7 have each wanted since
2026-08-09". Half of that was wrong: U7 has carried a load average
(12.9 / 23.9) in its job/flavor field since that date. What U7 records
as unmeasured is narrower --- whether the shared `CARGO_TARGET_DIR` and
its sibling builds PRODUCED that load. So 34.04 is the first
contemporaneous reading for a U6 occurrence and a second data point
beside U7's, not the registry's first. The row title oversold it too.
U15's disposition list also omitted U15.
The gate line named `2c24303` while the branch head was `6ddce0f` ---
the same staleness review caught one round earlier, when it named
`db24ae3`. Naming one commit reproduces the defect on the next commit.
It is a table now: one row per gated head, so the next update adds a row
instead of rewriting a sentence, and the staleness is stated rather than
waiting to be found again.
Three corrections, all mine, all caught in review.
U14 claimed a second occurrence for a run whose selector set had
ROTATED --- `full_buffer_summary_flatten` and `dired_renders_10k_entries`
in place of `grep_supersede` and `acc34_purge`. This file's own matching
rule requires the exact selectors to match, so that is a new incident.
It is now U15. The `04-lib-crdt` pair the two runs share is recorded
where it belongs, as U6's own occurrence; U6 goes from one occurrence to
five, four of them on 2026-08-30.
U14 also said "three unrelated tests". There are four selectors.
And the load claim went too far. `/proc/loadavg` was read once, after
the second run, so there is no series to correlate against; the margins
are not monotonic (`composition_overhead` ran 1.182x, 1.592x, 1.527x,
and reports two different values within the second run); and an earlier
version said a load average of 34 "explains it without any help". What
34.04 establishes is severe unrelated load present CONTEMPORANEOUSLY
with one multi-red run --- a measured confound, not a measured cause.
That is still worth more than U6 and U7 have had since August, and it is
worth exactly that much.
The lane block also still carried the withdrawn "opposite way to R7"
claim and named `db24ae3` as the gate head. It now names `2c24303` and
log 20260830T193305Z-4167110.
The gate redded again in the same three stages, with a partly rotated
selector set --- one of them being a U7 selector. This time
`/proc/loadavg` was read at the failure: 34.04, with the CPU saturated
by an unrelated `lean` workload on this shared machine and no cargo,
rustc or gate process of mine left running.
U6 and U7 have each recorded, since 2026-08-09, that the load confound
"was not measured, so it is a rival explanation, not a finding." It is
measured now, and the margins move with it monotonically across three
runs of one unchanged tree: 1.343883ms, then 1.689259ms, then
2.269247ms, against a 1ms budget. A regression does not get 69% worse
between two runs of the same tree.
This retires nothing. The budgets are still wall-clock assertions whose
measurement design nobody has defended --- R1's disposition, applied to
five more tests. What changes is that "one loaded machine" is now a
measured explanation rather than a plausible one.
The enumeration is the contract. Clippy's `match_same_arms` would
collapse the three `Ok(())` rows into one alternation, which is exactly
the conflation this lane exists to remove --- it would stop the table
from showing that `(forward, empty, None)` and `(history, empty, Some)`
are valid for OPPOSITE reasons.
The lint reached CI rather than the gate because the gate's clippy step
runs default features, so `#[cfg(feature = "crdt")]` code is never
linted locally. Five consecutive green gate runs could not see it. The
gap is recorded in the ledger; fixing it means adding a second clippy
flavor to shared gate infrastructure, which is its own lane.
The gate run verifying revision 5 redded three unrelated tests in three
stages: a 50ms supersede budget in `lib`, U6's pair in `lib-crdt`, and
an LSP readiness race in `sweep`. Recorded as U14, because the
co-occurrence is the signature --- three subsystems failing in one run
is far less likely than one loaded machine, and no selector reds twice.
It also falsifies something I had committed an hour earlier. U6's
second-occurrence block said the row "runs the OPPOSITE way to R7",
resting on both failures being out of gate while `04-lib-crdt` was green
in four gate runs. The next gate run redded `04-lib-crdt` with exactly
that pair. Four green stages were a run of four, not a property. The
claim is withdrawn in place rather than edited away, and U6's status
moves to a third occurrence: three in one afternoon, twice out of gate
and once in.
U14 also declines an R1 match it could have claimed. The `lib` failure
carries R1's required fragment but a different selector, and this
registry matches on both. Worth noting separately: the sibling test
already reports the elapsed value R1's row records as missing from its
own assertion --- the cheap half of what R1 defers is written next door.
R7's eighth-occurrence write-up said the gate's ambient root, TMPDIR and
cross-stage process state were "now the only place the difference can
be". That is wrong. The paired runs exclude the SOURCE TREE and nothing
else: scheduler load, kernel and socket timing, page cache pressure and
whatever else the machine was doing also varied between them, and a
BrokenPipe on a socket handshake is exactly what those can drive. The
three remain the candidates worth varying one at a time --- because they
are the ones this project can vary --- not an exhaustive causal set.
U6 gained a second occurrence, and for the first time it REPRODUCED:
both selectors, both fragments, two consecutive runs. Margins recorded
per U11's lesson --- 1.343883ms against 1ms, and 1.182x against 1.10x.
Its asymmetry runs the opposite way to R7's: both failures were out of
gate, while the same command as `04-lib-crdt` was green in all four of
this lane's gate runs. Whatever the two rows share, it is not a
direction.
Framing revision 5 and the lane block are updated to match, including
the stale "AWAITING APPROVAL. Nothing implemented." header and the gate
line that named a commit the branch had already moved past.
Review found the predicate conflating the two things this lane exists to
separate. It called every empty-range/zero-insertion edit `version_only`
and then accepted `(History, empty, None)` through a wildcard arm ---
which contradicts the lane's own ruling that the op must survive, and
contradicts the public contract's "carries Some, and must".
The rule is now a full enumeration over provenance x text delta x
crdt_op. An EMPTY TEXT DELTA is a shape, not a verdict: both paths reach
it, and `crdt_op` is what separates them.
forward + empty + None valid, a syntactic no-op
forward + empty + Some invalid, the original bug
history + empty + Some valid, a version-only edit
history + empty + None invalid, the version advance is gone
C5 asserts all four rather than two. Both new quadrants were
mutation-checked and both fire; neither is caught by the proptest,
because no generated input reaches either --- which is the same reason
C5 was a directed injection to begin with.
The public `Edit` doc was also factually false. It said forward
`apply_edit` never produces the empty-delta shape while C2b proves all
three forward empty forms do. It now names the shape and says which path
yields which `crdt_op`.
Two consecutive gate runs on one worktree, minutes apart. Heads differ
by a single commit touching a single markdown file. The first was all
eight stages green; the second redded at `gpu` with all three of R7's
required fragments, and at `sweep` with the same single test.
The fifth occurrence excluded the observing tree relative to `main` by
having a documentation-only diff. This pair excludes it relative to the
immediately preceding GREEN RUN OF THE SAME GATE on the same worktree,
which is strictly sharper --- whatever varies across that green/red
boundary, it is not the source tree.
No ratio is claimed from it. Folding verification gates into the
2026-08-29 window is exactly the drift that window was bounded against.
Five isolated selector runs were green, which per this file's own rerun
rule and the seventh occurrence's correction establishes intermittence
and excludes nothing.
The block claimed the fixture had verified that "replicas stay converged
--- the op IS broadcast". That was inspection of the call sites, not
execution: nothing had ever replayed the op on a replica, and text
equality alone cannot see a lost version advance. It also called the
buffer-end range location "genuinely arbitrary either way"; the census
rules it, and finds it weakly preferable.
Both corrections are recorded in place rather than quietly dropped,
along with what each review round caught and the two mutation
asymmetries the framing asserts.
`undo_crdt_mode` and `redo_crdt_mode` diff two ropes. When the operation
being inverted was an identity replace, those ropes are equal, so the
derived edit has an empty range and zero insertion — while still
carrying the `crdt_op` that `crdt.undo()` produced. The proptest read
that shape as "a no-op edit with an op" and redded.
The behaviour is right; the invariant was mis-scoped. It now takes
`(OperationClass, &Edit)`:
- a FORWARD version-only edit must carry no op, unchanged in strength
and still unreachable, because the three syntactically empty `EditOp`
forms short-circuit at `is_no_op_edit` before the CRDT path exists;
- a HISTORY version-only edit may carry one, and must, or the version
advance the replicas need is lost.
Implements `docs/crdt-identity-undo-framing.md` revision 4, C1-C9.
The two contracts worth naming here:
C3 replays the history op on a REMOTE replica seeded with the forward
ops, asserting materialized text AND version vector. Text alone does not
discriminate: dropping the op leaves the text identical. The existing
round-trip proptest excludes history ops precisely because replaying one
onto an unseeded replica is ill-posed; seeding is what makes this well
posed.
C4 is three witnesses because the obvious one is vacuous. C4a counts the
broadcast, since "unchanged" is also what a missing broadcast produces.
C4b executes the census. C4c pins the style-span guard with a synthetic
INTERIOR empty edit — at the buffer end, where the real history edit
lands, deleting that guard changes nothing, so the mutant would have
survived.
Also updates the public `Edit` contract, which enumerated three shapes
and had no fourth, and the fixture's own doc comment, which presented
convergence as verified when nothing had replayed it.
A CRDT-version delta and a text delta are independent dimensions of
`Edit`. The invariant that says otherwise was written for
`is_no_op_edit`, a forward pre-check that `undo_crdt_mode` and
`redo_crdt_mode` never reach.
Approved at revision 4, after four review rounds. What the rounds
changed, since the record is worth more than the conclusion:
- revision 2 answered the question instead of posing it, and narrowed
what the existing fixture actually established (broadcast reachability
is by inspection, not replay);
- revision 3 completed §4's consumer census rather than deferring it,
and two of its results corrected the framing's own claims: `TextView`
is not buffer-attached at all, and two consumers already carry
explicit empty/empty guards written for other reasons;
- revision 4 replaced C9's file-set-and-count guard, which a same-file
substitution walks straight through, with an exact `(file, impl
target)` assertion scoped to in-tree implementations.
Three witnesses exist because a green suite is not evidence that a suite
RAN: re-ignoring a fixture (C1), dropping a loop parameter (C6), and
adding an unclassified consumer (C9) are all silent under ordinary
assertions.
I recorded the dispatched run's proptest red as "a DETERMINISTIC red",
"not like anything else in this registry --- a property violation with a
concrete witness, not a load artefact", and proposed committing the
proptest seed as the first step. All of that was wrong, and it was wrong
for a plain reason: I recorded a finding without checking whether main
already documented it.
src/buffer.rs:3005 carries an #[ignore]d deterministic fixture,
crdt_undo_of_an_identity_replace_reports_a_no_op_edit_carrying_an_op,
that reduces this exact case. The shrunken input is a randomly sampled
recurrence of it: Replace(5, 1, " ") replaces the trailing space with
itself, a textual no-op but a real CRDT delete-plus-insert, so Undo
emits a version-advancing operation with no visible text change.
Committing the seed was a second error. It duplicates a deterministic
fixture that already exists and would only make a disputed assertion
fail permanently instead of occasionally.
The fixture already verified what I would have re-derived: content stays
correct, replicas stay converged because the op IS broadcast and neither
crdt_op consumer short-circuits on an empty range, and the cursor does
not jump.
The entry now frames the actual decision --- whether a visible text delta
and a CRDT-version delta are independent dimensions of Edit --- and the
evidence a lane owes: that forward textual no-ops still produce no
operation, that any permitted empty-text undo operation carries valid
bytes and preserves remote replay convergence, and an explicit
disposition of derive_replacement_edit reporting the empty range at the
buffer end rather than the edit site.
The interruption is bounded and does not reorder the roadmap. GUI arc 1b
remains the next product lane.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016bqGA6s9tTUFzYpbeW3tai
D3 PASS. Dispatched run 33307137965 on main: all 14 jobs started and
concluded success or failure, none skipped, cancelled or timed_out. The
run's own conclusion was failure, which D3 permits by design --- its
contract is that the matrix RAN, not that it passed.
D2 PASS, with every guard exercised rather than assumed. A reached
in_progress; B was dispatched while A ran; the REQUIRED OVERLAP WAS
OBSERVED, A in_progress and B pending, so this is not the vacuous
version that passes when A finishes first; headSha(A) == headSha(B) ==
7b82e14, so the two really shared ci-<sha>; and A concluded success, not
cancelled. B was cancelled afterwards by hand to save macOS minutes, and
its fate is not part of D2.
An unplanned observation is worth more than either witness: a main PUSH
run was already in_progress at 7b82e14 when the first dispatch was
issued, and the dispatch QUEUED BEHIND IT rather than cancelling it,
starting only once the push finished. That is the same-SHA
push/dispatch interaction the framing described and no witness covered.
The first dispatch also immediately found a red on main, which is what
the lane was built for --- and it is not like anything else in this
registry. rope_matches_crdt_projection_after_arbitrary_edits failed a
proptest assertion at src/buffer.rs:3120, "no-op edit must have crdt_op
= None (Undo)", and SHRANK to a four-operation minimal input. That is a
property violation with a concrete witness, not a load artefact. It is
recorded as its own lane with the selector, the assertion, the shrunken
ops, the regression seed, and the reason a local pass is not evidence:
no .proptest-regressions file is committed, so the failing case is not
replayed and one green local run means nothing.
Not investigated here. This lane's scope was one workflow key.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016bqGA6s9tTUFzYpbeW3tai
Recorded together, because the control is what the occurrence owed.
The occurrence: Test (macos-latest / lua54) on head d9cc0fa, 1984 passed
and 1 failed, panicking at src/async_runtime.rs:2444 with R1's required
fragment. THIS IS R1 despite the new Lua flavor --- the row records
luajit and this is lua54, and flavor is not part of signature matching.
The selector and the fragment are, and both match.
The control: the same job at the branch's EXACT merge base 2e9f62b,
rerun the same hour, green at 1985 passed and 0 failed. Both logs were
preserved before the rerun, so neither result is reconstructed from a
conclusion --- the selector is read as "... ok" in the control's own log.
What that green control establishes is carefully bounded. It does NOT
establish environmental cause and does NOT retire R1; it shows only that
the merge base can pass the same job in the same hour. A RED control
would have established that the branch did not introduce the occurrence,
and that inference is simply unavailable here. R1 remains live either
way under its measurement-design disposition.
Records that R1's assertion omits its measurement --- the same class #244
fixed, and a line my discarded sweep had surfaced --- and that it is
deliberately NOT fixed here. Adding the elapsed value would sharpen the
next failure's evidence and repair nothing about the measurement design
this row is about: that a thread::sleep(15ms) is asserted by comment to
mean the worker picked the job up. That belongs to the async-runtime
lane with the rest of Q#MCI3.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016bqGA6s9tTUFzYpbeW3tai
Adds workflow_dispatch: to ci.yml. One key, no inputs, and no job,
matrix, step, permission or timeout changes --- a dispatched run does
exactly what a push run does.
THE SCOPE IS ONE REGISTRY ROW, and the framing took three revisions to
get that right. CI never invokes scripts/gate --- zero occurrences ---
so every local-gate red already has a merge-base control that needs no
CI at all: the exact failing command, with its flags, at the recorded
merge base, in a separate worktree. U11 alone needs this trigger,
because it is macOS-specific and this project has no Mac. When it
recurred on #243 the only contemporaneous main-side control available
was a re-run of a run eight days old.
What this is NOT: it does not run scripts/gate, does not vary R7's gate
conditions, and does not select U9's alternate test commands. Those need
their own changes. This is infrastructure they would build on.
The concurrency interaction is stated at its real strength: once a
dispatched run is in_progress, a second same-SHA dispatch will not
cancel it. cancel-in-progress: false protects a RUNNING run and not a
pending one, and a main push and a dispatch at the same SHA do share
ci-<sha>. A PR run never shares a dispatch's group at all, so no
PR-involving test exercises the predicate.
D1 passes and bites pre-merge: the working-tree file parses and
workflow_dispatch is a key under `on`; dropping it fails the assertion.
The witness looks up both 'on' and the boolean True, because YAML 1.1
parses `on:` as a boolean and a naive d['on'] raises before checking
anything.
D2 and D3 are OWED POST-MERGE and recorded as such --- GitHub offers
workflow_dispatch only for a workflow already on the default branch, so
neither can run before this lands. D2 carries a void-and-retry rule so
it cannot pass when the two runs never actually overlap, and D3 requires
each job to conclude success or failure rather than merely not-skipped.
Framing docs/ci-manual-dispatch-framing.md revision 3, approved.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016bqGA6s9tTUFzYpbeW3tai
Merged 2026-08-29 at approved head 7b50682, merge commit a85205a, via
--match-head-commit. 14/14 CI green on that head, 8-stage local gate
green on it too.
Records what the lane exposed beyond its own change. THREE rows in one
session were written to assert a condition without keeping what would
explain its violation: the two parse budgets this fixes, and U13's
`let (out, _, _)`, which throws away a child's success status and stderr
so its failure cannot distinguish wrong-but-successful output from a
refused invocation. U13 is not this lane's to fix, but three instances
make it a pattern rather than an oversight, and each one costs a review
round to establish nothing.
Also records R7's sixth and seventh occurrences, the bounded observation
window that stops its ratio drifting with review activity, and the
correction that seventeen green out-of-gate runs are not exclusions ---
nothing outside the gate has ever reproduced R7, so matching one gate
condition at a time outside it cannot isolate an in-gate cause.
Per the standing rule, this absorption does not advance any canonical
base to its own commit.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016bqGA6s9tTUFzYpbeW3tai
The head-exact review gate reached sweep with six green stages, then
`skipped_directories_are_reported_with_a_reason` observed empty child
stdout. The durable failure cannot say why because the row discards the
child status and stderr.
Record the exact signature, the structurally unrelated branch diff,
and the isolated-selector and full-binary green reruns at their actual
strength: intermittence only. Diagnostic hardening remains a separate
lane.
The four-run observation window contains two green in-gate runs, so it
cannot be described as ending at the first green. Name it directly as
the first four in-gate runs; the timestamps continue to define the
boundary exactly and later verification gates remain excluded.