Commit Graph

1543 Commits

Author SHA1 Message Date
Levi Neuwirth 2dccc2b69c
feat(gui-1b): B6's routing rows --- the minimap scrolls the document
B6's mechanism landed with B1: WheelTarget::Minimap maps to its own
ResidualOwner, and the horizontal leg already skips it. What was owed is
the evidence, and apply_wheel had none --- the accumulator had unit rows,
but nothing drove a wheel through dispatch_window_event to the effect.

Three rows on EffectHarness, which holds a real App and a real daemon
socket, so the classifier, the accumulator and the local step are all
production code:

- one notch over the minimap scrolls the document viewport by one notch
  and re-declares it, exactly as over text;
- a part-notch over the minimap does not complete one over the document
  --- the surface-switch jump B1 forbids, and the minimap is its sharpest
  case because both surfaces move the same viewport, so sharing a bank
  would look harmless and produce a jump nothing explains. A third step
  gives the document bank the rest of its notch, so the row cannot pass
  by measuring nothing;
- a horizontal notch over the minimap is inert while the same notch over
  text scrolls sideways.

All three mutations fire, disjointly: an inert arm for the minimap fires
the first, ResidualOwner::Document fires the second, dropping the
horizontal guard fires the third.

The third row is why EffectHarness now takes a document. Its first
version asserted inertness over the default fixture and the setup
assertion caught it twice: four-column lines pin the saturated right
bound to zero, and wrapping --- on by default --- pins the left edge to
zero regardless of target. Either would have reported inertness the row
never tested. It now uses wide lines and turns wrap off through
LineWrapFacts, the message production uses, asserting the wrap landed.
`new()` keeps the 200-line fixture and its M22 rationale verbatim.

Gates: fmt; clippy --workspace --all-targets -D warnings (which caught a
float_cmp in the first draft of the inertness assertion); pmacs-gpu 312;
--lib 1997; --lib --features crdt 2190; git diff --check.
2026-09-01 20:29:48 +02:00
Levi Neuwirth a0705399e2
docs(lane): withdraw the unverified CI cache analogue
The local false compile red remains a machine-local artifact-state
hazard with no CI occurrence. If the same fragments ever appear in CI,
that run is a new incident and must be recorded under its actual job
and flavor, not the local bare-cargo flavor.

Withdraw the rust-cache explanation: workspace crates are not cached by
default and GitHub cache visibility is scoped. Neither replace it with
the equally unmeasured claim that CI builds fresh.
2026-09-01 20:21:55 +02:00
Levi Neuwirth 7a534eeeca
docs(lane): the registry does hold local reds --- fix the stated reason
3bd9ad1 justified keeping the shared-target false red out of
docs/ci-red-signatures.md by asserting the registry is for CI reds and
that a row would have to invent a job and flavor. Both halves are false.
The registry carries many `local (Linux)` rows with precise invocation
flavors --- one of them flavored "with sibling worktrees building
concurrently", which is adjacent to this very mechanism --- and this
incident's truthful flavor is plainly statable: local, bare
`cargo test -p pmacs-gpu`, globally shared target.

I asserted a property of a file I had not read. The conclusion happened
to survive; the reason did not, and a reason that cannot be checked is
the part that goes on to be reused.

The entry now keeps it here for the reason that actually holds --- a
machine-local artifact-state hazard with no CI occurrence, every one of
them from bypassing scripts/gate --- and records that a CI appearance
would belong in the registry with that flavor. It also declines the
easy immunity claim: CI restores artifacts through rust-cache from a
cache shared across branches, so the same mixing has a plausible CI
analogue that has simply never been observed. The superseded reasoning
is named in place rather than quietly replaced.
2026-09-01 19:59:38 +02:00
Levi Neuwirth 3bd9ad14e7
docs(lane): the shared target directory's false compile red
Records, in this lane's infrastructure notes rather than
docs/ci-red-signatures.md, a red that has now fired twice locally. The
registry is for CI reds; a row there would have to name a job and
flavor this has never had.

The signature is E0433, "cannot find `columns` in `pmacs_protocol`",
four errors from a bare `cargo test -p pmacs-gpu`. Both times the module
was entirely present --- columns.rs matching HEAD, `pub mod columns;` at
lib.rs:40 in worktree and HEAD alike. Nothing was missing to find.

CARGO_TARGET_DIR is exported globally here, one directory for every
checkout, and `main` has no columns module at all --- the sharing
refactor is unmerged 1b work. So a pmacs-protocol artifact built without
the module shares a target directory with the branch that needs it. The
entry states what was confirmed (the shared directory, the module's
absence on main, the red clearing) and says plainly that which build
deposited the stale artifact was not determined.

Three of the four error sites are recorded. The fourth scrolled past the
captured tail, so the entry says so instead of naming a likely
candidate.

The control is `scripts/gate` and its per-worktree CARGO_TARGET_DIR, not
the touch that cleared it --- with the warning that the touch papers
over a genuine deletion exactly as readily, and that the two reds look
alike. agent-handoff.md:2609 already carries the general bare-cargo
hazard; it did not carry this signature, count, or control.
2026-09-01 19:43:21 +02:00
Levi Neuwirth bd0815c240
docs(gui-1b): the reshape-tail mutation fires five rows, not three
The snapshot geometry row enumerated what dropping
`apply_panel_cursor_icon()` from `reshape`'s tail would fire: "this row,
the menu row, and the line-number row below." Two panel rows have since
joined them, so the list read as exhaustive while being short by two.

Executed the mutation at this tree: it fires the two snapshot rows, the
line-number row, and both panel rows. The doc block now says so, and
says the list is expected to grow --- one hook reached by more routes is
the design, not drift, and a fixed enumeration would go stale at every
new one.

Verified: 12 B5 rows green; fmt, workspace clippy --all-targets
-D warnings, and git diff --check clean.
2026-09-01 19:39:42 +02:00
Levi Neuwirth cc07642b04
docs(lane): record the completed B5 panel geometry seam
Advance the GUI Stage 1b ledger to the immutable code head and correct
the central-hook claim. Counting reshape callers missed two panel-band
transitions: accepted frame changes and geometry epoch invalidation.

Record the shared inset-change seam, the content-only repaint exception,
the 309-row GPU pass, workspace clippy/fmt/diff verification, and all four
executed mutations.
2026-09-01 19:18:39 +02:00
Levi Neuwirth 175cc7be16
fix(gui-1b): settle B5 across panel geometry transitions
The B5 cursor icon was re-derived in reshape, but accepted PanelFrame
messages changed the document boundary without reaching reshape. A
stationary pointer therefore kept Text over a new panel, or RowResize
after the panel disappeared.

Route panel inset changes through one helper that synchronizes dimensions
and reshapes. Keep content-only panel frames on the repaint path, so normal
panel output does not rebuild the document. Geometry epoch advancement uses
the same helper because it disowns the retained frame after resize/font
handling has already reshaped against the old presentation.

Add production-path witnesses for Present/Absent, an independent epoch
invalidation row, and a discriminator keeping content-only frames off the
reshape path. All four documented mutations fire.
2026-09-01 19:08:05 +02:00
Levi Neuwirth 392703bfc9
docs(lane): B5 lands, and the shape of how it kept not landing
Moves B5 from "owed outright" to landed at `a4627cd`, and records where
its icon re-derivation ended up: `reshape`'s tail, rather than the two
call sites that had been noticed.

The recovery-relevant part is the failure mode, not the fix. Twice the
patch went to a path someone had pointed at, and twice a second applier
in the same arm restored the icon anyway --- so the row passed with its
documented mutation applied. That is not a weak witness. It is a row
that measures nothing while reading, in the diff, exactly like a row
that measures something.
2026-09-01 14:43:05 +02:00
Levi Neuwirth a4627cdfbb
fix(gui-1b): B5 --- re-derive the cursor icon where geometry settles
The previous commit applied the icon in the `BufferSnapshot` arm after
its reshape, and routed menu writes through a `set_menu` helper that
applied it too. Both were wrong in the same way: they patched the two
paths that had been noticed instead of the place the paths converge.

Two consequences, both found in review.

The snapshot's documented mutation did not fire. Replacing
`set_menu(None)` with `self.menu = None` still passed, because the
unconditional post-reshape apply in the same arm restored `Text`. The
two appliers masked each other, so the single-writer property was an
inspection claim wearing a witness's clothes, and the row's stated
mutation named something the row could not see.

And the snapshot is only one geometry transition. The line-number mode
moves `text_left` through its own path; minimap arrival, panel
appearance, window resize and font metrics move the text clip the same
way. A stationary pointer could go from text to gutter, minimap, panel
or outside with the icon still saying `Text`. Ten call sites reshape;
three applied the icon.

So the icon is now re-derived in `reshape`'s tail --- the one point
every geometry settle already passes through --- and the two ad-hoc
geometry appliers are gone, `set_menu` with them. Three production
appliers remain, one per cause: pointer motion, menu ownership with no
geometry, and geometry.

Each is separately witnessed; the matrix is disjoint. Dropping the
`reshape` tail fires the two snapshot rows and the new line-number row;
dropping the `MenuPrompt` applier fires the lifecycle row alone;
dropping the every-motion applier fires the motion row alone.

The new row drives `InstanceMessage::LineNumbers` --- the production
arm, not `apply_panel_cursor_icon` directly --- turning the gutter on
under a stationary pointer and asserting the pixel it swallows stops
being an I-beam. Its setup asserts the gutter actually moved past the
probe, so the row cannot pass by measuring nothing.

Gates at this tree: fmt clean; clippy --workspace --all-targets
-D warnings clean; pmacs-gpu 306 (+1); --lib 1997; --lib --features
crdt 2190; pmacs-protocol 32; git diff --check clean.
2026-09-01 14:42:26 +02:00
Levi Neuwirth 7ed5c42d9d
fix(gui-1b): B5 --- the snapshot path closes the menu too
`MenuPrompt` was not the only writer. `BufferSnapshot` clears the menu
--- a popup anchored in the prior buffer would hijack input --- and left
the icon alone, so an open-menu arrow survived a buffer replacement over
document text until the pointer moved.

Menu state now has ONE writer, `set_menu`, which re-derives the icon.
A third site added later gets it for free instead of reintroducing the
same defect, and `self.menu = ` appears exactly once in the crate.

The snapshot also needs the icon re-derived AFTER the reshape, for a
different reason: it changes geometry. `text_left` is
`TEXT_LEFT + gutter_width_px` and the gutter is sized from the line
count, so a snapshot moves the text boundary under a stationary pointer
--- a pixel that was gutter becomes text.

BOTH MECHANISMS FIRE ON THE SNAPSHOT PATH, so at first each masked the
other: removing either alone left every row green, and only removing
both fired anything. That is the "one omission at a time" defect R4/R5
is written to avoid, reproduced here. Each now has its own witness:

  set_menu's apply removed     -> 2 rows (the MenuPrompt legs)
  post-reshape apply removed   -> 1 row  (the new geometry leg)

The geometry row moves the line count between one and four digits so the
gutter widens and narrows under a stationary pointer, and asserts the
icon follows. Its setup steps assert the gutter actually moved, so a
fixture that stopped discriminating fails rather than passing quietly.
2026-09-01 14:31:01 +02:00
Levi Neuwirth e8bc6750ae
fix(gui-1b): B5 --- the icon follows the MENU'S LIFECYCLE, not motion
The menu case was wrong in a way the motion-path patch only hid.

MENU OWNERSHIP CHANGES WITH NO POINTER MOTION. `MenuPrompt` opens and
closes the menu, and re-deriving the icon only on motion meant opening
while an I-beam showed left it on screen over the menu, and closing left
the arrow over text --- in both cases until the pointer happened to
move. The icon is a function of the state, so it is now re-derived where
the state changes.

That makes the motion-path call REDUNDANT, and it is removed rather than
kept: motion inside an open menu changes no ownership, and a second
writer there is one no row could distinguish from the first. This is the
option review offered, and it is the better half --- the transition is
where the fact lives.

The new row drives `apply_attach_message` --- the production path ---
and asserts `last_cursor_icon`, the value actually written, across both
transitions with the pointer never moving. Dropping the `MenuPrompt`
call fires it. The decision-half row stays separate so a failure says
whether the DECISION or the APPLICATION broke; dropping the
`menu.is_some()` guard fires both.

And the blank-area row documented a mutation that was not the one run.
`hit_test_source_byte` takes `&mut self` and the helper is `&self`, so
the literal substitution does not compile; the executed mutation bounds
`x` by the glyphs' extent, which is the same semantics geometrically.
The row now records what was executed and why the literal one is
unavailable.
2026-09-01 14:23:33 +02:00
Levi Neuwirth 6ad0c448b1
fix(gui-1b): B5 --- witness the motion path, the blank, and the menu
Three of B5's claims were asserted nowhere that could fail.

THE EVERY-MOTION FIX WAS UNWITNESSED. Both rows called
`desired_cursor_icon` directly, so reinstating the divider-change gate
left them green --- the gate is on the caller. A new row drives
`apply_cursor_moved` from text into the gutter with `hover_divider`
false throughout and asserts `last_cursor_icon` changes. Reinstating the
gate fires it.

THE GEOMETRIC-VERSUS-BYTE RULING WAS UNWITNESSED. The only positive
point sat over an actual glyph, so a byte hit-test passed. A row now
puts the pointer well past a short line's end, inside the text
rectangle, and requires `Text`. Bounding x by the glyphs' extent ---
byte-hit-test semantics expressed geometrically --- fires it.

THE MENU PATH LEAKED AN I-BEAM. `apply_cursor_moved` returns early while
a menu is open, so an I-beam showing when the menu opened stayed on
screen over the menu indefinitely. The menu now applies the icon on that
path and counts as chrome in `pointer_over_text_content`, with a row;
dropping the guard fires it.

`apply_panel_cursor_icon`'s doc still said it chooses between RowResize
and Default. It chooses among three, and says so, including why calling
it per-motion is cheap.
2026-09-01 14:16:27 +02:00
Levi Neuwirth 9f24dceb6a
feat(gui-1b): B5 --- an I-beam over text content, and nowhere else
§2a's CORRECTION 3 said where this had to land: `apply_panel_cursor_icon`
already owns the cursor and writes `Default` in its else branch, so an
I-beam at a separate site would be CLOBBERED by it on the next motion.
B5 extends that owner rather than joining it --- `desired_cursor_icon`
decides RowResize, Text and Default together or not at all, with the
divider outranking the I-beam because a drag handle is never text.

`pointer_over_text_content` is geometric, not a byte hit-test: an I-beam
belongs over the text AREA including the blank past a short line's end,
and a byte test would flicker along a ragged right margin. It excludes
the gutter, the minimap, the panel band and everything outside the
document's text rect, each for its own reason.

The icon now applies on EVERY motion rather than only when divider hover
flips. B5's transitions --- crossing the gutter, crossing the text's
right edge --- do not touch `hover_divider`, so the old gate would have
left the icon stale for exactly the cases B5 is about. The write is
idempotent against `last_cursor_icon`, so per-motion calls cost a
comparison rather than a platform round-trip.

THE FIRST VERSION OF THE ROW COULD NOT SEE ITS OWN MUTATION. With line
numbers off, `gutter_width_px` is 0 and `text_left == TEXT_LEFT`, so
"extend the I-beam over the gutter" changed nothing and the row passed a
broken build --- 0 rows fired. The fixture now turns line numbers on and
ASSERTS a gutter exists before relying on one. Both mutations fire:

  I-beam over the gutter    -> the coverage row
  I-beam outranks divider   -> the coverage row
  no-pointer guesses a spot -> the no-pointer row
2026-09-01 14:03:04 +02:00
Levi Neuwirth cfd8f6ba28
docs(lane): two precision fixes, and make 1b's block a real checkpoint
The block said the platform decision "replaces a `cfg!` read". It does
not: production still reads `cfg!(target_os = "linux")` at
pmacs-gpu/src/main.rs:3450. What became injectable is the SELECTION
decision taken after that target fact --- `is_linux` comes from `cfg!`
in a non-test build and from an override otherwise, and
`paste_source_for(is_linux)` is the part a row can drive both ways.

And "L1-L8 still owed" hid implemented state that recovery needs. The
manual authority latch, its wrap/replacement clearing, and R4/R5's two
separate residual resets have all LANDED; what is owed is their
witnesses. The block now splits "landed but not yet witnessed" from
"owed outright" rather than letting one phrase imply neither exists.

It also names the authoritative ref and the exact recovery command,
which is what this file is for and what the block did not have.
2026-09-01 13:47:26 +02:00
Levi Neuwirth 5dd201485c
docs(lane): open 1b's ledger block, with three infrastructure gaps
The lane has been implementing without a ledger entry, which is the
recovery state this file exists to carry. It records the base, the
framing revision, what has landed, and what is still owed --- including
B1's disposal half, which the code documents as absent rather than
stubbing.

Three findings are recorded because each cost review time and none is
this lane's to fix:

- `scripts/gate`'s clippy step runs default features only, so crdt-gated
  code is never linted locally;
- NOTHING runs `cargo doc` anywhere, so broken intra-doc links are
  ungated repository-wide --- including one pre-existing, in a file this
  lane never touched;
- no non-Linux CI leg runs `pmacs-gpu` tests, which is why B4's platform
  decision is an injected parameter rather than a `cfg!` read.

Each names the lane that would own the fix rather than proposing it
here.
2026-09-01 12:48:44 +02:00
Levi Neuwirth f4f3d63bc1
docs(gui-1b): repair B4's stale references and a duplicated sentence
Deleting `middle_click_paste_source` left a broken intra-doc link on
`apply_middle_press` and a stale name in the end-to-end row's comment.
Both now name `paste_source_for`, which is what the code calls.

And `an_unused_button_produces_no_effect_of_any_kind`'s opening sentence
was duplicated on one line --- the tail of the same insertion that split
it in the first place. Repaired.

WHY NEITHER WAS CAUGHT, which is the part worth keeping: NOTHING RUNS
`cargo doc`. It is absent from `scripts/gate` and from every ci.yml job,
so broken intra-doc links are ungated across this repository. `git diff
--check` cannot see them because they are syntactically valid, and
clippy does not read doc links. Running it by hand here confirms my link
now resolves --- and surfaces one PRE-EXISTING unresolved link,
`MathNode` at pmacs-gpu/src/math_layout.rs:314, in a file this lane has
never touched.

I have not added a doc step to the gate: that is shared infrastructure
and its own lane, alongside the clippy default-features gap this lane
already recorded. The finding is carried to 1b's ledger block rather
than left in a commit message.
2026-09-01 12:48:19 +02:00
Levi Neuwirth 1799aa28c8
fix(gui-1b): make B4's off-Linux leg actually run, and assert whole Steps
MY "IT RUNS ON THE NON-LINUX CI LEGS" NOTE WAS FALSE. `cargo test -p
pmacs-gpu` appears exactly once in ci.yml, in the Ubuntu-only
`gpu-render` job; the macOS matrix tests the workspace default member
only. So the `unwrap_or(PasteSource::Clipboard)` mutant was green in
every environment that actually executes --- and I wrote that note one
commit after writing about honesty, which is the part worth recording.

The platform is now a PARAMETER rather than a `cfg!` read inside the
decision: `paste_source_for(is_linux)`, with an injectable override on
`App` for tests. The off-Linux branch runs on this host, and a new row
asserts the gesture is completely inert there. The mutant fires it
locally, not hypothetically.

AND "WHOLE TRANSCRIPT" WAS STILL OVERSTATED. Both arms filtered for
`Paste`, so any other outbound event passed, and the release assertion
had the same hole. All three assertions are exact `Step` equality now:
one PRIMARY paste and no local effect on Linux, completely empty off
Linux, completely empty on release. The frontend id is read from the
transcript rather than assumed, so the row pins payload and shape
without pinning an id the handshake owns.

Three mutations, each firing locally:

  unwrap_or(Clipboard)  -> the off-Linux row
  dispatch arm no-op    -> the end-to-end row
  source = Clipboard    -> both

I have not touched ci.yml. Adding a macOS `pmacs-gpu` leg is a change to
shared infrastructure and belongs in its own lane; making the contract
testable where the tests already run was the fix available here.

One slip of mine, fixed in the same change: the first version of this
commit left `middle_click_paste_source` dead --- `apply_middle_press`
calls `paste_source_for` directly now --- and I committed with clippy
failing because I ran the gates after `git commit` rather than before.
The helper is gone, the seam row names both platforms explicitly, and
the gates ran first this time.
2026-09-01 12:39:26 +02:00
Levi Neuwirth 200fc16ba2
fix(gui-1b): assert B4's off-Linux inertness where the effect would be
The inertness stopped at a seam. `middle_click_paste_source()
.unwrap_or(PasteSource::Clipboard)` at the call site restores the
rejected fallback and passes every row: the helper still returns `None`,
and Linux still receives PRIMARY. A contract asserted only in the
function that decides it is not asserted on the path that acts on it.

The end-to-end row drops its `cfg(target_os = "linux")` and asserts the
complete transcript on both platforms: one PRIMARY paste on Linux, and
off Linux NO paste of any selection and no local effect either.

One honest limit is recorded on the row rather than left implied. On a
Linux host that `unwrap_or` never engages --- the source is already
`Some(Primary)` --- so no row on this machine can fire that mutant, and
a green local run says nothing about it. The `else` branch is what
catches it, and it runs on the non-Linux CI legs. Forcing the source to
`None` everywhere fires two rows locally, which is the closest
demonstration available here.

Also repairs the neighbouring test's documentation, which my insertion
had split: `an_unused_button_produces_no_effect_of_any_kind` was left
with "row that calls it claimed-and-dropped" while its opening two lines
had been absorbed into the B4 row's comment. Both are contiguous blocks
again.

Two process notes, because both recurred:

- This is the THIRD insertion in this lane to damage an adjacent test's
  docs or attributes. The cause is anchoring a splice on a `fn` or doc
  line without checking what precedes it; from here I anchor above the
  doc block and read the neighbour back after inserting.
- The previous commit's message claimed the `cfg` removal it did not
  contain: an edit script died partway, wrote nothing, and I committed
  on the strength of a later partial edit. Amended rather than left
  standing, and the file is now verified per claim rather than per
  script exit.
2026-09-01 12:22:57 +02:00
Levi Neuwirth 3a4a748316
fix(gui-1b): B4 --- witness the production effect, drop the unframed fallback
TWO MUTATIONS LEFT BOTH B4 ROWS GREEN. Changing the source to
`Clipboard`, or replacing the dispatch arm with a no-op, was invisible:
one row asserted `middle_click_paste_source` in isolation and the other
asserted `route_pointer` in isolation, and nothing asserted the effect
the gesture produces. Two seams tested separately are not a path tested
once.

A third row drives a middle press through `dispatch_window_event` and
asserts EXACTLY ONE outbound `Paste` carrying the PRIMARY payload, and
that the release sends none. PRIMARY and CLIPBOARD are stubbed with
DISTINGUISHABLE contents, which is the point --- identical stubs would
pass with the wrong selection read.

That needed a seam: `State::set_test_selection`, consulted by
`read_os_selection` before the OS clipboard. A test-only field in
production code is a cost, and it is the smallest one that makes B4's
actual contract --- WHICH selection --- assertable without a real
clipboard. Both mutations now fire: source-to-Clipboard fires two rows,
the no-op dispatch fires the end-to-end row.

AND THE OFF-LINUX FALLBACK WAS UNFRAMED BEHAVIOUR I INVENTED. B4 rules
"PRIMARY on Linux" and rules nothing else. The gesture was inert on
every other platform; my previous commit made it paste the CLIPBOARD
there, and the row adopted that choice permanently. `middle_click_paste_source`
now returns `Option`, `None` off Linux, and the gesture stays inert. A
fallback needs framing and re-approval, not a default chosen while
implementing.
2026-09-01 12:14:16 +02:00
Levi Neuwirth 493db8f965
feat(gui-1b): B4 --- a middle click pastes the PRIMARY selection
`PointerRoute::UnusedButton`'s own doc named this row: "Stage 1b's B4
gives the middle button a meaning (PRIMARY-selection paste on Linux) and
lands here." B4 splits that variant, as §2a said it would.

A middle PRESS is now `PointerRoute::MiddlePress` and reads the PRIMARY
selection, shipping it as the same `Paste` wire operation Ctrl-V uses.
Its RELEASE stays unused, like the right button's --- the paste happens
once, on the press.

PRIMARY and CLIPBOARD are different selections with different contents:
the clipboard holds what was last explicitly copied, PRIMARY holds what
is currently selected. Reading the wrong one still produces a paste,
just not the one the platform convention promises, so the row asserts
the SOURCE rather than that a paste happened.
`middle_click_paste_source` is the seam that makes that assertable
without an OS clipboard; `read_os_selection` takes the source and uses
arboard's `GetExtLinux` for PRIMARY.

Two rows, three mutations, each firing:

  source = Clipboard        -> the source row
  middle press unrouted     -> the routing row
  release also pastes       -> the routing row

Three existing rows encoded the old behaviour --- that a middle press is
semantics-free. They are updated to keep testing what they SAY rather
than being weakened to accommodate B4: the routing row now covers
Back/Forward/Other plus the middle RELEASE, and the two effect/order
rows switch to `Back`, a button that still has no semantics. Widening
them to accept the new meaning would have left no row asserting that
semantics-free buttons stay inert.
2026-09-01 11:43:30 +02:00
Levi Neuwirth 5371229b9a
test(gui-1b): the split row now guarantees its own setup
The row claimed to target the inactive pane and did not. It hit-tested
a hardcoded column, took whichever pane that resolved to, and then
branched its assertions to match. If the layout ever put the active pane
under that column, an active-window-routing mutant would pass and the
focus assertion would be a tautology --- the row would look like
evidence while proving nothing.

It now SCANS for a cell the hit-test actually resolves to the inactive
pane, asserts `under_pointer == other` before dispatching, and asserts
directly with no adaptive branch:

  the inactive pane moves by three
  the active pane's origin is unchanged
  the active window id is unchanged

The setup guard is itself falsifiable: pointing the scan at the active
pane makes the row fail on "setup: the pointer must be over the INACTIVE
pane" rather than passing quietly. That check matters more than the
mutation --- a setup assertion nothing can break is the same defect one
level up.

The routing mutant still fires the row, and only it.
2026-09-01 11:32:48 +02:00
Levi Neuwirth 951c6447b3
test(gui-1b): make B7's witnesses discriminate what they claim to
Four evidence blockers. The implementation was right; the rows could not
tell right from several wrongs.

THE RIGHT BOUND WAS A RANGE, NOT THE CONTRACT. `origin + viewport >=
widest` plus `origin < widest` admits every origin from `widest -
viewport` through `widest - 1` --- almost the entire viewport blank, and
passing. It now asserts the exact value, `widest.saturating_sub(
viewport)`. The `widest - 1` mutant used to pass and now fires two rows.

THE "SATURATES AT ZERO" ROW ONLY TESTED THE LOWER CLAMP. Its document is
400 columns wide, so it never exercised a buffer NARROWER than the
viewport --- the case where `widest - viewport` underflows. The framed
narrow-buffer row is added: short content, ten right notches, origin
stays zero.

THE FIXTURE FORGED THE WRAP CACHE. `paint_once` painted under the real
default (wrap) and then wrote `last_wrap = Truncate` directly.
Production fills that cache from `ui.line-wrap` during paint, so a row
built on a forged value proves nothing about the path that fills it.
`set_line_wrap` now sets the buffer-local setting, `paint_truncated`
paints and ASSERTS the paint recorded truncate, and the wrap row paints
a genuinely wrapped state at origin zero and shows a right notch is
inert. Deleting the wheel guard still fires it, and only it.

ALL FOUR ROWS USED ONE WINDOW. A mutant routing horizontal scroll to the
ACTIVE window instead of `win_id` passed every one of them, and B2
requires the document surface under the pointer --- horizontal state is
per-window. A split-window row wheels over the pane the pointer names,
asserts only that pane's `view_left` moves, that the other is untouched,
and that focus does not follow. The mutant fires it, and only it.

`dispatch_mouse`'s documentation now covers the horizontal arms, their
bound, and that the pane under the pointer is the one that moves.
2026-09-01 11:18:38 +02:00
Levi Neuwirth db18261a5d
feat(gui-1b): B7 --- the TUI's horizontal axis, which used to vanish
`MouseEventKind::ScrollLeft` and `ScrollRight` reached `dispatch_mouse`
and fell into its catch-all, so a horizontal wheel over a TUI document
did nothing at all. B7's row said as much: "events arrive and are
dropped".

They now call `scroll_window_columns` --- the SAME helper B2's panel leg
uses. One contract, one implementation, so the two document surfaces
cannot clamp differently, which is what B3 means by "each document
surface clamps at its own content bounds".

Four rows, each mutation-checked against its own mutation:

  three columns per notch      step of one          -> 1 row
  right bound keeps the last   clamp at full width  -> 1 row
    display column visible
  origin saturates at zero     (covered by the above)
  wrap pins the origin to 0    drop the wrap guard  -> 1 row, only it

The right-bound row is the one that needed care. "The origin stopped
somewhere" is satisfiable by a bound that lets the origin pass every
glyph and blanks the viewport, so the row asserts the FINAL DISPLAY
COLUMN IS STILL REACHABLE --- origin + viewport >= widest --- and
separately that the origin has not passed the content.

Two fixture facts the rows need, both stated rather than assumed:

- they PAINT first. B7's bound is `widest - viewport`, and a window that
  has never painted has `last_content_cols == 0`, so the bound
  degenerates to `widest` and the row measures nothing. My first draft
  had exactly that hole and passed for the wrong reason.
- they set TRUNCATE after the paint. Under wrap the bound does not exist
  and clause 5 pins the origin to zero, so a bound row must be a
  truncate row. The paint writes `last_wrap` from the view config, which
  is why the order matters --- setting it first, as I first did, is
  overwritten and the rows silently exercised the wrap branch instead.
2026-09-01 11:00:20 +02:00
Levi Neuwirth 364eb7b095
refactor(gpu): the rich-text projection delegates too
A THIRD copy of the rule lived in the projection that decides where the
GPU actually renders a later tab: manual `stop - column % stop`
arithmetic, and a per-character advance calling `UnicodeWidthChar::width`
directly. So the previous commit's mutation broke the minimap while
leaving the rendering path untouched --- the shared bound could still
drift from the columns the GPU draws at.

Both now delegate to `pmacs_protocol::columns::advance_char`: the tab
width is DERIVED from the shared advance rather than recomputed, and the
per-character step is the shared one.

The projection's stream semantics stay local, because they are real and
distinct: the column runs ACROSS chunks, so adornment text shifts a
later tab, and a newline restarts it. That is why the wrapper still
exists rather than being replaced outright.

Evidence, not assertion: mutating the tab stop in
`pmacs_protocol::columns` now breaks BOTH
`tab_projection_uses_shared_stops_and_unicode_columns` and
`minimap_columns_match_code_tab_and_unicode_widths`. Six adornment rows,
including `caret_projection_accounts_for_inline_adornments`, still pass,
so the stream behaviour survived the delegation.

`TAB_STOP_COLUMNS` and `UnicodeWidthChar` are now unused imports in
pmacs-gpu and are dropped --- which is itself the check that no copy of
the rule remains in this crate.
2026-09-01 10:42:07 +02:00
Levi Neuwirth 9e54cd2c5b
refactor: put the display-column rule where both frontends share it
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.
2026-09-01 10:34:00 +02:00
Levi Neuwirth d3d720ba8a
feat(gui-1b): B2's horizontal leg --- the panel origin actually moves
`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.
2026-09-01 10:07:23 +02:00
Levi Neuwirth 0f29530dd8
fix(gui-1b): notch units, minimap inertness, document-wide bound
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.
2026-09-01 00:00:59 +02:00
Levi Neuwirth 2d19713b0e
feat(gui-1b): B1's per-target fractional wheel residual
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.
2026-08-31 22:54:44 +02:00
Levi Neuwirth 204998351b
docs(framing): revision 20 --- the both-axis witness was vacuous
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.
2026-08-31 22:24:12 +02:00
Levi Neuwirth 8b8b6982ae
docs(framing): revision 20 --- three blockers from review
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.
2026-08-31 21:51:34 +02:00
Levi Neuwirth 12b37742a7
docs(framing): revision 20 --- §2a re-measured at 0ec13b3
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.
2026-08-31 21:40:14 +02:00
Levi Neuwirth bd595bd148
docs(framing): revision 19 --- reconcile two lineages that both said 13
`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.
2026-08-31 21:15:49 +02:00
Levi Neuwirth 6882ec5e96
docs(framing): mark Stage 1 revision 18 approved
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
2026-08-31 21:14:35 +02:00
Levi Neuwirth fff1779677
docs(framing): Stage 1 revision 18 --- residuals that outlive the buffer, and two overclaims narrowed
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
2026-08-31 21:14:35 +02:00
Levi Neuwirth 836f417313
docs(framing): Stage 1 revision 17 --- B1's other two separations, and a false concurrency claim withdrawn
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
2026-08-31 21:14:35 +02:00
Levi Neuwirth cf63f63a07
docs(framing): Stage 1 revision 16 --- the replay lane becomes a hard prerequisite
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
2026-08-31 21:14:35 +02:00
Levi Neuwirth a330658feb
docs(framing): Stage 1 revision 15 --- B1 fully ruled, and three defects in 14
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
2026-08-31 21:14:21 +02:00
Levi Neuwirth e94ce85be1
docs(framing): Stage 1 revision 14 --- Q#S1-11 ruled B, and two holes closed
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
2026-08-31 21:14:12 +02:00
Levi Neuwirth ce982e6701
docs(framing): Stage 1 revision 13 --- 1b ground truth, and Q#S1-11
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
2026-08-31 21:13:55 +02:00
Levi Neuwirth 0ec13b3953
docs(ci-reds): U16's third occurrence, and the first on `main`
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.
2026-08-31 21:02:45 +02:00
Levi Neuwirth 90238fbe51
docs(lane): #246 merged as 78346de
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.
2026-08-31 20:56:49 +02:00
Levi Neuwirth 78346de19a
Merge pull request #246 from levineuwirth/crdt-identity-undo
Key the crdt_op shape invariant on provenance, not on the Edit's shape
2026-08-31 20:56:09 +02:00
Levi Neuwirth 093d677c05
docs(ci-reds): U19's title repeated the claim its body withdraws
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.
2026-08-31 20:28:58 +02:00
Levi Neuwirth 03204cc8af
docs(ci-reds): U19 WAS rerun, and three more overstatements withdrawn
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.
2026-08-31 20:17:45 +02:00
Levi Neuwirth ea786a2878
docs(ci-reds): U16 reproduced, and U19 is new
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.
2026-08-31 19:47:09 +02:00
Levi Neuwirth 7ce03018f8
docs(ci-reds): R7 has TWELVE occurrences --- absorb the two owed ones
`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.
2026-08-31 19:40:57 +02:00
Levi Neuwirth 28c2ad4463
docs(ci-reds): renumber R7 by date, and five corrections
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.
2026-08-31 17:26:47 +02:00
Levi Neuwirth 7d819902ff
docs(ci-reds): R7's ninth occurrence
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.
2026-08-31 16:25:11 +02:00
Levi Neuwirth 65b40897bd
docs(ci-reds): fix the serial-run arithmetic, U9's scope, and add U18
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.
2026-08-31 16:17:43 +02:00
Levi Neuwirth a7c4b3adec
docs(ci-reds): U9's control is VOID --- cargo runs test binaries serially
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.
2026-08-31 15:07:22 +02:00