Skip to content

feat(web): land the remaining Clean and Cogent UI/UX work as one integration - #364

Merged
gcko merged 20 commits into
mainfrom
spacedock-ensign/ui-integration
Sep 18, 2026
Merged

gcko merged 20 commits into
mainfrom
spacedock-ensign/ui-integration

Conversation

@gcko

@gcko gcko commented Sep 17, 2026

Copy link
Copy Markdown
Contributor

Implements DRC-4589 — Split the three inks onto label, value and absence roles so a label stops sharing its ink with its own answer
Implements DRC-4593 — Invert the briefing's emphasis and let its all-absent grid collapse
Implements DRC-4591 — Adopt a three-tier caveat rule and put the long form behind redraw-safe disclosures
Implements DRC-4594 — Reorder Held to so its purpose and its inputs come before the caveats
Implements DRC-4592 — Make each tab name its own panel and carry a derived state cue
Implements DRC-4597 — Rebuild the scope rail card around the session title
Implements DRC-4598 — Re-expose the timeline's own filter instead of shipping one of its modes as a dead-end tab
Implements DRC-4595 — Put Console's operations first and lift the steer box out of TRIPWIRES
Implements DRC-4596 — Make the asset test enforce font size, not the contrast that already passes

The remaining Clean and Cogent UI/UX work, landed as one PR because all four branches rewrite styles.css and all four move the three frontend byte-pin oracles. Landing them separately would resolve the same conflict four times against a file moving underneath each resolution.

Why one PR

AGENTS.md (Calibrating Effort) says the only constraint that genuinely forces a split is that exactly one PR may touch cargento_runtime/web/. Four branches wanted it. One integration resolves the conflict once with all four intents visible, which is what surfaced the defects below.

What changed

  • Four ink registers in the one :root, with label, value, absence and caption rules repointed. No colour moves.
  • An absence separates from its value on shape, never on tone: a family swap and a leading em dash stamped at emission, because no selector can tell a null from a real 0.
  • The briefing's emphasis is inverted. FO INSPECTING is a mono chip at the label tier with a one-line gloss; the line saying whether the captain is needed takes the sentence tier.
  • A three-tier caveat rule, with long form behind redraw-safe disclosures, and Held to reordered so purpose and inputs come before caveats.
  • Each tab names its own panel and carries a derived cue, with loading and unpublished marks distinct from a zero.
  • The scope rail card is rebuilt around the session title; the timeline's own filter is back, remembered per session.
  • Console leads with the operations rail; the steer composer is built once, in the project chrome.
  • The asset test enforces font size, with the label floor, the sub-11px registry and the sentence floor each demonstrated failing by a mutant.

Three defects only the integration could find

Each passed on its own branch. None is findable by reading a single diff.

A withheld session title rendered in full ink, identical to a published one. DRC-4597 moved data-next-withheld off a <small> and onto span.next-cockpit-scope-title, which declares color:var(--ink) at the same (0,1,0) specificity and later in the sheet. DRC-4589's AC-3 had stripped the colour from the scope-rail override on the reading that it was redundant. It is not: with it gone the title resolved to --ink — the absence-outranks-its-value inversion this milestone exists to remove. AC-3's "exactly one rule colours a withheld value" was a count standing in for a property, and two separate rule-counting tests passed over the defect because neither resolved the pair. WithheldTitleKeepsTheAbsenceInkTest resolves both branches through css_cascade and was confirmed to fail when the declaration is removed. This reverses a captain's ruling made when the premise was different, and is the one thing here that most wants a second opinion.

DRC-4591's two COUNTS readers stopped seeing half their rows. Their regex has no room for an attribute, and DRC-4589 then stamped data-next-absent on exactly the absent spans, so both silently asserted against three values where the render has five.

The Now briefing's word cap read 101 against 100. Neither branch overran it alone: the authority gloss is thirteen words and the steer composer twenty-six. The composer is not briefing prose, though, and the mirror regex was anchored on the last </section> before the tabs nav, so it swallowed a control. The lookahead now stops at the briefing's own close. The cap stays at 100: raising it to absorb a measurement error would retire the constraint without anyone deciding to.

Every count in this area was wrong

The sentence-tier figures were taken against the pre-squash DRC-4587 tree, which raised thirteen rules the narrower #361 that merged did not. main said sixty-seven, forty-nine and eighteen. The branch adding the assertion said seventy, forty-seven and twenty-three. Measured on the merged sheet: sixty-one, across sixty distinct selectors — two selectors are declared twice, which is why NextPageAssetContractTest asserts both the set and the count rather than either alone. The capped-versus-uncapped split is deleted rather than recomputed: nothing asserts it, so it was precisely the un-held prose figure main was right to refuse. Two independent counts, both confidently specific, both wrong, neither caught by review. The sub-floor inventory had the same defect inverted: its four sub-group figures were right but it omitted the --fs-xs group entirely, because on its tree those fourteen had already been raised. Thirty-four, not nineteen — thirty-four rather than thirty-three because DRC-4591's .pc-substrate-steps fix moved that rule below the floor. NextPageAssetContractTest now asserts all of it.

Review round two — 8b9b57a8

Six code findings from Copilot, each triaged by execution rather than by reading, because this milestone has twice agreed with a finding's reasoning and then refuted it by running it.

Refuted, no change made. projectAction's graph-mode ternary drops decisions, but the function has no caller and the data-calm attribute it serves has no dispatcher in the bundle; the live button is rewritten to data-next-cockpit-action="graph-mode" and passes its argument through the validating projectSetGraphMode (measured: decisions → all → decisions). And the unknown HOW IT LANDED card renders a stated absence rather than a blank slot — nextObservedPair falls back to the reason, so endText/claimText are never empty.

Fixed. The re-entry disclosure said "Why a raise is unavailable" one line under "Its terminal can be raised". The Decisions cue reported an unloaded context entry as "decisions not published" while the panel said "Loading semantic context" — pending existed and was reached only at session scope. The Console setup summary told a reader whose server was started with both capabilities on that both were off, because an unread capability collapsed into false; measured on a fixture serving an enabled model and a registered terminal, unread read false/false where settled read true/true, and the terminal section sat inside the setup disclosure on that render. Both fields are three-state now.

Two sentences claimed the observed record is above them. DRC-4594 moved it last and reworded "the one sentence that was positionally anchored" — so it swept by known string and missed NEXT_READING_OFFER, false on every render, and NEXT_READING_UNAUTHORIZED, false whenever it renders.

Four criteria verified over lists are now derivations. HeldToPositionalSentencesTest sweeps every positional sentence the bundle emits, reads each one's direction, and compares rendered indices on the correct side. It found a third instance on its first run: the two settle cues say "the question above", and nextCockpitConflict draws them inside its own <header>, above the question. A positional word is kept and required to be true rather than deleted, because a sweep with nothing to find passes forever; four falsifiers were run against it and all four kill it. The Held to lede is measured against every absence element the tab renders; two axes, read separately is checked over every .py, .js, .css and .html under cargento_runtime rather than two files; and the caveat-survival set is derived from the merge base's own reading-why paragraphs, with the revision and extraction recorded and re-derived by a test wherever the blob is reachable.

The absence register guard matched 8 of the 14 rules that carry a marker. Four spell it with a single hyphen, and all four spelled their ink var(--ink3) directly, so repointing --ink-absence would have moved seven rules and left four behind. The sweep derives its subject now and the four are repointed; two grouped selectors were split because each shared a rule with something that is not an absence.

One defect the fix introduced, caught by reading the diff — 2fa5a2f4

The three-state capability read keyed off the loading flag rather than the state beside it. projectTerminalLookup marks a registered terminal {state:"registered", loading:true} while it re-checks, and it re-checks whenever the payload revision advances — every poll. So the first draft reported null on every poll of a working console, flipped the summary to "terminal bridge not read yet", and moved the live terminal back into the setup disclosure it had just left. Measured before and after: settled {terminal:true}, one revision later {terminal:null} with the terminal inside the disclosure; now {terminal:true} across the refresh with the section still outside it.

No suite caught it, which is why it now has one. ConsoleSetupNeverCallsAnUnreadCapabilityOffTest pins all three states, asserts the refresh really is in flight before comparing, and was confirmed to fail on three mutants: the loading read, the original Boolean() pair, and a version that reports an unavailable terminal as unknown rather than off.

This is the reason there are two commits rather than the one intended. The head described below is 2fa5a2f4; the six review replies cite 8b9b57a8, which is that head minus this fix.

Post-verdict round — F1 from the DRC-4589/DRC-4593 reviewer — 9959f623

nextCockpitTabCue keyed off entry.data; the panel beside it keyed off entry.data and entry.error, and the poll's .catch() is the only writer of error. A first fetch that failed therefore left the panel saying "Semantic context unavailable." next to a cue reporting pending, glossed "decisions not loaded yet" — a completed, failed read described as still in flight. The function's own comment asserted the opposite.

Fixed rather than filed because nextCockpitTabCue does not exist on origin/maina54451d6 introduced it in this PR, so it is a regression created here.

Reproduced at both scopes, one more than reported, and the focused arm diverged a second way: the panel also requires the project entry when a session is focused, and the cue did not. Two conditions that must agree is the shape that produced both, so there is now one nextCockpitContextRead and neither caller re-derives it. The cue takes its facts from that same read at both scopes instead of from observation — the two were the same object (measured ===), so no board changes; what goes is the second read that let state and facts drift.

A failed read gets its own cue state rather than borrowing one: not pending, which claims a read is still coming, and not unobserved, which claims nothing was published. The mark is the em dash this sheet already uses for a slot with no figure in it.

The obvious test for the new variant does not work, and that is worth recording. Pinning the emitted class name passes whether or not the sheet styles it — measured, deleting the --unavailable rule left the entire cockpit module green, with only the byte pins red, and those fire on any stylesheet edit. EveryTabCueVariantIsColouredByItsOwnRuleTest derives the variants from the producer and resolves each through the cascade against the unstyled cue, so a state added with no rule reds instead of rendering an absence in the ink a real figure gets.

Seven mutants run, all seven fail. Two that appeared to survive were re-run after confirming the substitution had applied; both then failed.

Post-verdict rounds — 26223372 then ddd422bf

M1's key now carries the project and the session. The session alone was the collision; the project alone silently destroys the per-session distinction the resolver reads at session scope, and that mutant passed every test the first post-verdict commit added — it has one of its own now. projectLoadGraphModes validated values and not key shapes, so the legacy {"": "all"} a shipped browser already holds is dropped once at load rather than defended against at every read; the separator is what makes that shape decidable. AC-4's storage test is rewritten on the two-project fixture, not patched: it seeded the collided key and asserted the first render took it, and on a one-project board that key reads as "project scope" rather than "every project".

The context map is separated across all five reachable shapes, cue and panel resolving one classification. The .catch() merges rather than replaces, so a failure over loaded data is invisible to .data alone; that state keeps its last known rows per DRC-4613 and is separately classified so the issue can act on it. Agreement is not coverage — the two agree, one condition governs both, and DRC-4613 is open.

projectTerminalLookup preserved only the affirmative answer, so a default bridge-off console flipped to "not read yet" every poll — the third instance of this shape, inside the repair for the first. Its test samples on the render that starts the re-check, because a sample four microtasks later passes on the broken code.

.next-guardrail-copy small rendered at 12.5px inside the compliant set. Taken structurally: the compliant set is resolved on elements, and the straddle precondition is pinned as a census.

Seven oracle repairs, each with its own falsifier. AC-7 is derived over every web/*.js rather than widened by one tab — a second fixture still cannot fail on the surface nobody thought to add.

Two things this PR does NOT claim. The element-resolved guard does not close DRC-4595's AC-5 hole: it builds its path from the selector, not the page, and widening it by hypothesising every matching DOM was measured and abandoned at 8,873 false positives. DRC-4597's AC-6 is not re-measured at 980px — the viewport would not leave 1260 on this desk, and a figure taken there would be the error the reviewer was credited for refusing.

Final round — c88cc110, test and docs only

AC-3 had two halves and checked one. That the count rows stay out of the disclosure was asserted; that the disclosure has anything in it was not, so the criterion's own empty-body falsifier passed. Removing nextCockpitWhy's empty-body return leaves 3652 behavioural tests green and draws exactly one <p class="next-cockpit-reading-why"></p> in the re-entry block — a reachable arm emitting an empty sentence.

AC-5's matrix collapsed a second time, in my own repair. The capability stub returned {content:"tmux"} and nextFocusCapability requires typeof meta.getAttribute === "function", so all six cases drew the capability-off branch: one raise sentence, no disclosure, one branch of three. Every per-case guard passed, because the anchor and both rows render on every branch — which is why guarding each case cannot see six copies of one. The six are now asserted to differ: three raise claims, two re-entry sentences, six distinct pairs.

--ink-caption's doubling onto --ink3 is now recorded in design-next-ui.md; the ruling cited for declining a three-hexes assertion covered only --ink-label and --ink-absence. And _below_floor returns its skip count (77 swept / 77 resolved / 0 skipped) — recorded as a tripwire, not a live guard, because resolve raises on an unsupported rule before the skip branch is reachable.

Evidence

Everything below is measured at c88cc110, which is the head this describes.

  • Dashboard suite 3639/3639 passed, scripts suite 515/515 passed, coverage 86.8% against a floor of 73. Load average under 4 during the run; no failure appeared in any of the three contention-prone modules, so none needed a solo re-run.
  • ruff check, ruff format --check, mypy --strict, lint_embedded.py, validate_plugins.py, bump_version.py --current all clean. No version field moved. claude plugin validate --strict and agy plugin validate both pass.
  • All nine byte pins regenerated from the assets rather than reasoned about: assembled 965_309 / e79d000c…, styles.css 121_011 / f1d8a9bc…, project.js 111_842. test_next_page, test_next_flag and test_focus all green.
  • All twelve checks were green on 8b9b57a8 and again on 2fa5a2f4; this head re-runs them. Sub-floor census 35, up from 34 at b2675a2, because one grouped selector was split into two. Sentence tier unchanged at 61. docs/design-next-ui.md says 35.
  • Every new guard was run against a reintroduced defect before being committed, and two mutants that survived were re-run against the source's actual line wrapping before being believed.

Review guidance

The six commits were cherry-picked, not merged. Three of the four branches were based on 2a07380, which is not the tree that merged: PR #362 landed as a squash of 1d847b0, built on the squashed #361, while 2a07380 sits on the pre-squash DRC-4587 commits. The two differ by 399 insertions across seven paths including styles.css and a whole file (tests/css_cascade.py) absent from 2a07380. The resolution rule throughout was to take main's line wherever the only difference was one of those superseded promotions, and apply only the delta each commit itself introduced. That is worth spot-checking; it is where a wrong call would hide.

Three absence pairs that DRC-4592 and DRC-4597 appended to AnAbsenceNeverOutranksTheValueItReplacesTest no longer fit it, since main rewrote that class to resolve DOM paths and narrowed it to sentence-tier absences. Rather than drop them, they are carried in AnAbsenceNeverRendersLargerThanItsValueTest. DRC-4589's delegation pair was dropped at first on the same reasoning and then restored verbatim, after its implementer showed by mutation that the stamp class does not subsume it: the stamp constrains only the absent variant and says nothing about the base rule, so the absence can be raised to any size with every stamp assertion green. Measured at 40px against the 32px figure.

No Closes #NNNN lines: DRC-NNNN are Linear issues, not GitHub ones, and GitHub issue #4589 does not exist in this repository. The Implements [DRC-NNNN](…) form above matches #361 and #362.

🤖 Generated with Claude Code

gcko and others added 11 commits September 18, 2026 00:57
Four registers in the one existing :root block, and the label, value, absence
and caption rules repointed at them. Twenty-one cockpit and session label rules
each spelled var(--ink3) in their own declaration block, so moving the label
tier meant editing twenty-one rules by hand and hoping none of them was a value.
Nothing changes colour: --ink-label and --ink-absence both resolve to --ink3
under the captain's ruling of 2026-09-17, --ink-value to --ink.

An absence separates from its value on shape, never on tone. COUNTS and
DELEGATION keep their own ink and gain a family swap plus a leading em dash from
[data-next-absent]::before, stamped at emission because no selector can tell a
null from a real 0. The three kinds of absence a reading paragraph can state
carry data-absence and are told apart by a left rule: dim solid, bright solid,
dotted, all three legible in greyscale.

The briefing's emphasis is inverted back. FO INSPECTING returns to a mono chip
at the label tier with a one-line gloss beside the heading, and the line saying
whether the captain is needed takes the sentence tier at full ink. The evidence
caption is suppressed when no task was observed, which is what leaves the label,
value and caption in three distinct registers, and "captured" leaves the
briefing on a two-verb rule that introduces no new absence phrase.

All nine byte pins recomputed from the assets across three files. The absence
side of every ternary is compared against its own value rather than asserted
alone, which is the defect class that cost this milestone four review cycles.

Implements DRC-4589
Implements DRC-4593

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Signed-off-by: Jared Scott <jared.scott@variable.team>
…rail card

Three issues on one branch, because exactly one in-flight PR may touch
cargento_runtime/web/ and all three do.

DRC-4592 - make each tab name its own panel and carry a derived state cue.
A lede per tab at the sentence tier, naming its own tab word, because three of
five open onto a heading that does not repeat the label. A derived cue on the
label in four states: a figure, a collection read and found empty, one nothing
has published, and one whose context has not arrived. Course counts
context.project.changes, Decisions the decision facts off the same semantic
nextCockpitDecisionSummary selects, and Held to session.departures under the
lane rule at nextCockpitDepartureLaneCount, which is now lifted out of
nextCockpitDepartures so the section and the cue cannot disagree. Now and
Console render no cue, and neither does Held to with the store off: those
panels render no collection, which is not an absence to assert. Every read is
guarded on the collection being an array first, because a length taken off a
list declared on every row reports the schema (DRC-4559, the first Measured
Invariant). Departure and revision are defined inline at first use; the reading
definition renders only on the arms where NEXT_READING_OFFER does not.

DRC-4598 - re-expose the timeline's own filter. nextCockpitTimeline stops
passing mode:"decisions", controls:false and passes defaultMode instead, so an
untouched tab renders what it rendered before and a reader who presses a button
reaches the filtered activity view and the all-events view. One resolver,
projectResolveGraphMode, is shared by the renderer and the cockpit heading, so
the heading cannot say RECORDED DECISIONS over an all-events list. The mode
mirrors to one cargento.next.graph.mode key and is read back on load. The dead
nextCockpitProjectScope is deleted; nextCockpitProjectScopeKind survives.

DRC-4597 - rebuild the scope rail card around the session title. Two lines: the
title alone on line 1 at --fs-sm in --ink, clipped with the whole string in
title=; harness, state, age and, on a twin row, the sid on line 2 at --fs-2xs.
The builder takes a value and a meta rather than a label and a subtitle,
because the project row and the session rows hold their value in opposite
slots. The word SESSION moves into the accessible name and the round marker and
connector rule carry the kind without it. Moving the sid off the title repairs
the withheld-title marker, which the exact-match test missed on every twin row.
The harness hoists into the heading when every row shares one.

Both branches of every value-and-absence ternary resolve at one size. The cue's
three state rules restate their size so the pair check can see them, and the
rail's withheld rule supplies font-family only, leaving the one board-wide rule
to own the colour.

Implements DRC-4592, DRC-4598 and DRC-4597.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Signed-off-by: Jared Scott <jared.scott@variable.team>
…orce size

DRC-4595. The Console panel spent its first 341px of 968 on setup prose for
two capabilities a default run has switched off, and the one place on the page
a person can write anything sat 86% of the way down, inside a section captioned
"local only, nothing enforces these". Nothing leaves the page: the panel now
emits the scope header, the project-scope prompt, the operations rail, then one
disclosure holding the terminal surface, the observer-model controls and the
status line. The summary reads the capability flags rather than whether those
bodies came back non-empty, because both return "" on routes where the
capability is on, and a capability that is on renders expanded and outside the
disclosure.

The steer composer is built once, in the project chrome, through the existing
renderer rather than retyped markup, so it keeps data-next-steer-form,
data-next-draft, data-next-controls-project, data-next-focus and the
500-character cap. The dead nextProjectControls is deleted: it had no caller in
web/ and built a second composer, which made "renders once" unprovable. The
correction no longer waits for the press, and the field was raised with the
caveat so the warning is not drawn larger than the words it warns about.

DRC-4596. The asset test measured the palette, which already passes on every
pair, and measured size nowhere. It now enforces the label floor, the exact
registry of sub-11px literals, and the sentence floor as a literal 15.0 with an
exact inventory of what still sits below it, each demonstrated failing by a
mutant applied to an in-process copy of the stylesheet. Its blind spot is
stated rather than implied away: it reads one rule at a time, so a sentence
whose size, family and line-height are composed across three rules is outside
it, and two such sentences render today.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Signed-off-by: Jared Scott <jared.scott@variable.team>
Two issues on one branch, because exactly one in-flight PR may touch
`cargento_runtime/web/`.

DRC-4591 — a three-tier caveat rule. Every caveat rendered as one paragraph in
the reading flow, and after DRC-4587 the caveat and the finding-label beside it
carried the identical font shorthand, so size no longer separated them at all
and the same sixty-nine words cost more vertical space than when the issue was
filed. `nextCockpitWhy` puts the claim inline and the rest behind a summary,
reusing `nextCockpitDisclosureAttr` so the existing restore lane reopens it
after a redraw with no registration; Console reuses `projectDisclosure` for the
same reason. Nothing is deleted: the one removal is the `two axes, read
separately` aside, which said what the footer under the same cards already said,
and its reason joins that footer. The rule is recorded as NUI-19, including why
tier 3 ships no `docs/` href and no `DEC-N` token — an installed plugin has no
`docs/` beside the page, and `RuntimeDecisionCitationsTest` reads the whole
runtime tree rather than its comments.

DRC-4594 — purpose and inputs before the caveats. A lede says what typing buys,
worded to the default board because the unasked lane is off unless the reader
started with `--unasked-readings`. The observed record moves from first to last,
which overturns half of the placement constraint recorded at `nextCockpitHeldTo`
deliberately: the sentence that was anchored to the record's old position is
reworded in the same change rather than left claiming a position it no longer
has. The re-entry paragraph becomes a primary action over two labelled rows with
the platform explanation behind a disclosure, the count labels drop the quantity
noun their new group heading supplies, and the `your words` sub-label goes —
DRC-4587 set it at the same size as the label it restated, so it was a second
label saying less.

`PairedBranchRegisterTest` is new and was not in either estimate. A value and
the absence that replaces it are chosen by a ternary and never render together,
so no selector sweep and no walk of a populated board can see a mismatch; it
resolves both branches through the cascade and compares them. It found one
open instance on this base, filed as DRC-4607 and pinned in `KNOWN_OPEN` rather
than fixed here, so a new violation fails the test and so does fixing that one
without moving it off the list.

All nine frontend byte figures recomputed from the assets across
`test_next_page.py`, `test_next_flag.py` and `test_focus.py`.

Implements DRC-4591, DRC-4594.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Signed-off-by: Jared Scott <jared.scott@variable.team>
The shipped skill body and the runtime module map both enumerated the tab in
its old order, with the observed record beside the two fields rather than after
the landing axes, and neither mentioned the lede. Both now read in render
order, and the skill body says what tiering does for a reader: the claim stays
on the page, the rest opens, and an opened one survives a redraw.

NUI-19 is retoned to the repository's voice standard. The rule itself is
unchanged.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Signed-off-by: Jared Scott <jared.scott@variable.team>
This branch carried its own resolver, written when `css_cascade.py` was not yet
on `main`. It is deleted rather than kept beside the merged one, which is
strictly more capable: real specificity ahead of source order, an element-path
walk that can resolve a branch declaring no size of its own, an error raised on
a selector form it cannot express instead of a rule silently dropped, and a
lookbehind that stops `font:10.5px/1.5` resolving as 0.5px. Two resolvers drift,
and the weaker one produces plausible numbers, which is the failure this guard
exists to stop.

Two checks survive because the census above cannot make them. A tier-2 summary
read against the body it hides is not a value/absence pair, and that test is
scoped to DRC-4587's claim and says so; the summary is still the same trap one
layer out, because it carries the only words a reader has for deciding whether
to open the body. And the COUNTS row emitter is asserted at the source, in the
shape `test_the_revision_slot_cannot_invert_because_one_class_carries_both`
already uses: by the time a second class reaches the stylesheet, a CSS-only
check is comparing two rules rather than noticing there is now a pair.

`RAISED_ABSENCES` held one entry, `two axes, read separately`, and this branch
deletes that span as a duplicate of the footer under the same cards. Removing it
silently would have left the tuple empty and its loop green over nothing, so it
is replaced by the landing card's own value and absence. It also did not fail
when the class went: `resolve` walks up the path and inherits, so the absence
still resolved to 15.0 off the section and compared equal to its value. That is
the class's own second docstring failure, "one built a DOM the application never
renders", reproduced in the test named for it. A path is only evidence while the
emitter still builds it, and no resolver can tell you that it does, so
`test_the_retired_axes_span_stays_retired` holds that half against both the
emitter and the stylesheet.

One inversion neither guard can see is recorded on the census docstring and
tracked as DRC-4607: a value inheriting its size, drawn against an absence that
declares a larger one.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Signed-off-by: Jared Scott <jared.scott@variable.team>
Integration fix across DRC-4589, DRC-4592 and DRC-4597, none of which could
see the others' trees.

DRC-4597 moved `data-next-withheld` off a `<small>` and onto
`span.next-cockpit-scope-title`. That class declares `color:var(--ink)` at
(0,1,0) -- the same specificity as the bare `[data-next-withheld]` rule and
later in the sheet -- so with DRC-4589's AC-3 having stripped the colour from
the scope-rail override, a withheld title resolved to FULL ink and rendered
byte-identical to a published one. That is the absence-outranks-its-value
inversion this milestone exists to remove, and it was reachable only by
resolving the pair: two separate rule-counting tests passed over it.

The override is (0,2,0) and is now the only rule that can hold the title on
the absence register, so it carries the colour again. AC-3's "exactly one rule
colours a withheld value" was a count standing in for a property; the property
is that every such rule resolves through the register, and that is what is
asserted now. `WithheldTitleKeepsTheAbsenceInkTest` resolves both branches
through `css_cascade` and was confirmed to fail when the declaration is
removed.

Two label rules added after DRC-4589 introduced the registers, by branches
that never saw them, still spelled `var(--ink3)` beside `var(--fs-label)` in
their own blocks. Repointed to `--ink-label`; no colour moves.

The DRC-4596 registries and the sentence-tier count are recomputed from the
merged sheet. The 70 the branch carried was read against the pre-squash
DRC-4587 tree, which raised thirteen rules the narrower #361 that merged did
not: main resolves 55 and the four branches here add seven, for 62. The
sub-floor inventory is 33, unchanged from main, so nothing here pushed a rule
below the floor. Each of the four below-floor absence rules was measured
against the value it replaces and all four pairs resolve 12.5/12.5.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Signed-off-by: Jared Scott <jared.scott@variable.team>
…other

Each of these passed on its own branch and fails only when the four are in one
tree, which is the class of defect a per-branch merge cannot surface.

DRC-4591's two COUNTS readers match `class="next-cockpit-count-value">`, with
no room for an attribute. DRC-4589 then stamped `data-next-absent` on exactly
the absent spans, so both readers silently dropped the two "not published"
rows and asserted against three values where the render has five. Every other
call site in this file already allows attributes; these two now match it.

DRC-4597's own withheld-title test asserts the count of one that this issue's
change refuted -- the attribute now sits on `span.next-cockpit-scope-title`,
which declares `color:var(--ink)` at the same specificity and later in the
sheet, so the rail override is load-bearing. It asserts the register on every
colouring rule instead, and expects the override to carry it.

DRC-4593's briefing-register test reads the size off a standalone
`.next-cockpit-recovery strong` rule. That rule carries no size on this
lineage: the narrower #361 that merged puts it on the head shared with
`small`. Repointed, and the three registers still resolve distinct.

The Now briefing's word cap read 101 against 100. Neither branch overran it
alone: DRC-4593's authority gloss is thirteen words and DRC-4595's steer
composer is twenty-six, and only the sum crosses. The composer is not briefing
prose, though -- it is a sibling `<section>` DRC-4595 moved into the project
chrome, and the mirror regex was anchored on the last `</section>` before the
tabs nav, so it swallowed a control and charged its words to the briefing. The
lookahead now stops at the briefing's own close. The cap is left at 100
deliberately: it states what the panel is for, and raising it to absorb a
measurement error would retire the constraint without anyone deciding to.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Signed-off-by: Jared Scott <jared.scott@variable.team>
Every hand-counted figure in this section was taken against the pre-squash
DRC-4587 tree, which raised thirteen rules the narrower change that merged did
not. `main` said sixty-seven, forty-nine and eighteen; the branch that added
the assertion said seventy, forty-seven and twenty-three. Measured on the
merged sheet: sixty-two, forty-four and eighteen. Two independent counts, both
confidently specific, both wrong, neither caught by review, which is why the
paragraph now says the figures are written down only because
`NextPageAssetContractTest` asserts them.

The sub-floor inventory had the same defect in the other direction. Its four
sub-group figures were right -- seven on `--fs-sm`, three on `--fs-body`, two
on `--fs-summary`, seven literals -- but it omitted the `--fs-xs` group
entirely, because on the tree it was written against those fourteen rules had
already been raised. Thirty-three, not nineteen.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Signed-off-by: Jared Scott <jared.scott@variable.team>
DRC-4592 and DRC-4597 each appended pairs to
`AnAbsenceNeverOutranksTheValueItReplacesTest` when it was a flat table of CSS
selector pairs. It was rewritten on the way in to resolve DOM paths through
the cascade and narrowed to the absences raised to the sentence tier, which
these three are not, so they no longer fit it and a textual merge would have
dropped them silently.

The property is true on the merged sheet -- the tab cue and both of its marks
declare 11.0, and the scope title is 14.0 against an 11.5 caption -- so the
guard is carried rather than discarded on the technicality that its old home
changed shape.

Confirmed failing: raising `.next-cockpit-tab-cue--pending` to `--fs-body`
reds it at 13.0 against 11.0. An earlier mutant that inserted `font-size:14px`
ahead of the existing `font:` shorthand did NOT red it, and correctly so --
the shorthand resets the size, so that mutant changed nothing a reader would
see. A guard is only worth committing once something has been seen to break
it.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Signed-off-by: Jared Scott <jared.scott@variable.team>
sync-docs pass for the nine UI/UX entities. Each claim was read off the code
rather than off the issue, and the four that were user-visible and undocumented
are now in the shipped body.

Console leads with the operations rail and keeps the startup capabilities in
one disclosure, with a capability that is on rendered expanded and outside it.
The tab strip names its own panel and carries a derived cue on the three tabs
that have one, with the loading and unpublished marks called out as distinct
from a zero. The scope rail leads with the session title over a harness, state
and age caption, says so when a title was never published, and carries the
session key only on rows that would otherwise render identically. The timeline
filter is back, with its three modes remembered per session in the browser.

The steer composer's move out of Tripwires is recorded where a reader would
look for it, since the control did not change but its location did.

COMPATIBILITY.md's docs-synced-through marker is deliberately untouched: this
is a feature branch, and parallel branches cannot vouch for one another.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Signed-off-by: Jared Scott <jared.scott@variable.team>
gcko added a commit that referenced this pull request Sep 17, 2026
…e syntax

The PR Workflow bullet said to always use explicit Closes #NNNN lines. Work
here is tracked in Linear as DRC-NNNN, which is not a GitHub issue number, so
Closes #4589 points at nothing today and will point at an unrelated issue once
this repository's own numbering reaches it -- it currently has #221 and #25 and
nothing else. The bullet predates the Linear move.

Found by the integrator for PR #364, which correctly used the
Implements [DRC-NNNN](url) form that #361 and #362 both use, and flagged that
doing so contradicted this file.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Signed-off-by: Jared Scott <jared.scott@variable.team>
@github-actions

Copy link
Copy Markdown
Contributor

Coverage

Name                                                                  Stmts   Miss Branch BrPart  Cover
-------------------------------------------------------------------------------------------------------
cargento/skills/cargento/agy_hook.py                                     79     14     28      7  78.5%
cargento/skills/cargento/cargento_runtime/__init__.py                     0      0      0      0 100.0%
cargento/skills/cargento/cargento_runtime/aggregate.py                  323      3    110      3  98.6%
cargento/skills/cargento/cargento_runtime/annotations.py                373     19    130     21  92.0%
cargento/skills/cargento/cargento_runtime/asks.py                       110      0     28      0 100.0%
cargento/skills/cargento/cargento_runtime/claude_data.py                340     40    158     18  88.4%
cargento/skills/cargento/cargento_runtime/cli.py                        212     20     52      6  89.4%
cargento/skills/cargento/cargento_runtime/collectors/__init__.py          0      0      0      0 100.0%
cargento/skills/cargento/cargento_runtime/collectors/antigravity.py     423     41    174     25  87.3%
cargento/skills/cargento/cargento_runtime/collectors/claude.py          305     17    112     15  91.8%
cargento/skills/cargento/cargento_runtime/collectors/codex.py           258     18     96     15  90.7%
cargento/skills/cargento/cargento_runtime/collectors/copilot.py         153      4     52      2  97.1%
cargento/skills/cargento/cargento_runtime/collectors/cursor.py          287     19    108     16  90.6%
cargento/skills/cargento/cargento_runtime/collectors/droid.py            34      3      6      1  90.0%
cargento/skills/cargento/cargento_runtime/collectors/gemini.py           54      7     16      4  84.3%
cargento/skills/cargento/cargento_runtime/collectors/goose.py            95      6     28      4  91.9%
cargento/skills/cargento/cargento_runtime/collectors/opencode.py        124      4     44      4  95.2%
cargento/skills/cargento/cargento_runtime/collectors/pi.py              328     34    152     20  88.8%
cargento/skills/cargento/cargento_runtime/config.py                     232      1     20      1  99.2%
cargento/skills/cargento/cargento_runtime/deliveries.py                 128     12     30      7  88.0%
cargento/skills/cargento/cargento_runtime/departures.py                 131      6     30      6  92.5%
cargento/skills/cargento/cargento_runtime/diagnostics.py                 88      3     26      3  94.7%
cargento/skills/cargento/cargento_runtime/dismissals.py                 111      3     32      3  95.8%
cargento/skills/cargento/cargento_runtime/ends.py                        89      5     18      0  95.3%
cargento/skills/cargento/cargento_runtime/events.py                     230      1     86      3  98.7%
cargento/skills/cargento/cargento_runtime/focus.py                       72      2     20      2  95.7%
cargento/skills/cargento/cargento_runtime/git_status.py                  59      1     22      2  96.3%
cargento/skills/cargento/cargento_runtime/history.py                    206      9     56      7  93.9%
cargento/skills/cargento/cargento_runtime/http_api.py                   933    107    300     37  88.3%
cargento/skills/cargento/cargento_runtime/interaction_prototype.py      709    221    198     38  65.7%
cargento/skills/cargento/cargento_runtime/io.py                         163      2     38      1  98.5%
cargento/skills/cargento/cargento_runtime/irreversible.py                39      0      6      0 100.0%
cargento/skills/cargento/cargento_runtime/lifecycle.py                  412     47    140     14  87.5%
cargento/skills/cargento/cargento_runtime/notifications.py              264     12     92      7  94.7%
cargento/skills/cargento/cargento_runtime/observation.py                412      6    122      3  98.3%
cargento/skills/cargento/cargento_runtime/observer.py                   388     24    156     15  92.8%
cargento/skills/cargento/cargento_runtime/probe.py                       44      0     18      1  98.4%
cargento/skills/cargento/cargento_runtime/project_context.py           1588    179    686    121  85.3%
cargento/skills/cargento/cargento_runtime/quota.py                      392      7    144      6  97.2%
cargento/skills/cargento/cargento_runtime/reach.py                       78      6     30      4  90.7%
cargento/skills/cargento/cargento_runtime/reading.py                    365      7    122      6  97.3%
cargento/skills/cargento/cargento_runtime/records.py                    265      5    118      9  96.3%
cargento/skills/cargento/cargento_runtime/semantic_history.py           290     14    110     18  92.0%
cargento/skills/cargento/cargento_runtime/sessions.py                   189      4     72      4  96.9%
cargento/skills/cargento/cargento_runtime/snapshot.py                    36      0      4      0 100.0%
cargento/skills/cargento/cargento_runtime/spacedock.py                  510     50    260     26  89.9%
cargento/skills/cargento/cargento_runtime/state.py                       83      0      2      0 100.0%
cargento/skills/cargento/cargento_runtime/stream.py                      57      0      8      0 100.0%
cargento/skills/cargento/cargento_runtime/transcripts.py                529     29    272     25  93.3%
cargento/skills/cargento/cargento_runtime/tripwires.py                  232     14     94     13  91.7%
cargento/skills/cargento/cargento_runtime/turns.py                      209     11    104     12  92.7%
cargento/skills/cargento/cargento_runtime/unasked.py                    119      2     34      1  98.0%
cargento/skills/cargento/cargento_runtime/web/__init__.py                 0      0      0      0 100.0%
cargento/skills/cargento/cargento_runtime/web/page.py                    38      0     10      0 100.0%
cargento/skills/cargento/event_hook.py                                  213     54     92     14  68.5%
cargento/skills/cargento/mcp_server.py                                  377     22    112     14  92.2%
cargento/skills/cargento/notify_hook.py                                  49     15      6      1  67.3%
cargento/skills/cargento/server.py                                        3      0      2      1  80.0%
cargento/skills/cargento/statusline_hook.py                             131     13     46      8  87.0%
scripts/bench_collect.py                                                214     13     54      6  92.9%
scripts/bench_event_latency.py                                           67     21     14      1  67.9%
scripts/bump_version.py                                                  60     12     24      5  77.4%
scripts/capture_focus_raise.py                                          729    131    236     16  79.4%
scripts/capture_hook.py                                                 292     30     90     11  88.7%
scripts/capture_team_registry.py                                        172     33     48      6  80.5%
scripts/capture_terminal_identity.py                                    347    108    104     14  66.3%
scripts/derive_prompt_shapes.py                                         210     16     88     14  89.3%
scripts/lint_embedded.py                                                 97      3     32      3  95.3%
scripts/mark_abstention.py                                              382    142    140     23  58.4%
scripts/score_abstention.py                                             493     52    182     18  88.4%
scripts/serve_operator_cockpit.py                                       448    191    112     20  54.5%
scripts/validate_plugins.py                                             762    202    434     67  70.8%
-------------------------------------------------------------------------------------------------------
TOTAL                                                                 18234   2097   6590    798  86.6%

Threshold: fail_under in pyproject.toml · label coverage-exception to bypass (visible in PR timeline).

Five findings from the implementers, each measured by them and re-measured
here against the consolidated tree rather than taken on report.

DRC-4589 showed its own `AnAbsentVariantBorrowsItsSizeFromTheValueItReplacesTest`
does not subsume the delegation tuple I dropped: that class constrains the
STAMPED variant and says nothing about the base rule it sits on, so raising
`.next-delegation-withheld strong` to 40px against a 32px figure left it green.
Its replacement is here and reds at 40px.

DRC-4592 showed the same for the rail card: with its title/meta tuple dropped,
redefining `--fs-2xs` above `--fs-sm` in `:root` survives every other check in
the file, because the surviving assertions pin which token each half uses and
cannot see the token move underneath them. Its four-line replacement reds on
that mutation.

DRC-4595 found a hole in its own sentence-tier count. A length passes a swap
where one rule leaves the tier and another joins at 15px, and THIS INTEGRATION
IS THAT SWAP: DRC-4589 moved `.next-cockpit-authority>span` off the tier and
`>small` on. The oracle would have been green on the change that defeats it.
It is a set now as well as a count, because two selectors are declared twice
and a set alone loses that.

DRC-4591 found a regression its own commit created: `.pc-substrate-steps` sat
at 15.0 beside a 12.5 sentence in the same disclosure, because #361's review
lowered the neighbour after the branch forked. Lowered to match.

Nothing tested DRC-4589's trap 1, that a colour tidied into the authority
override block would retire the captain-needed ink step by winning on source
order. It is the hunk I resolved, so it is guarded here, and the guard was
confirmed to fail when the colour is added.

The design doc carries three changes. DRC-4589's correction, that a register
makes a repoint safe and a removal dangerous, with its example rebased onto
the merged tree: the competitor it names was deleted by DRC-4592, and the live
instance is the rail card's own title rule winning on source order. The
sentence-tier paragraph now reports the set rather than a capped/uncapped
split that no test held. And main's refusal to state the sub-floor size is
restored, rescoped to the true total, because the set that IS asserted counts
what a per-rule census can see rather than what the page has.

Byte pins and both DRC-4596 registries regenerated after the stylesheet edit.
Every mutation check excluded the byte-pin oracle, which fires on any
stylesheet edit at all and would have reported all five as killed.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Signed-off-by: Jared Scott <jared.scott@variable.team>
gcko added a commit that referenced this pull request Sep 17, 2026
'Compare the set rather than its length' was recorded here as the fix for an
oracle that passes a compensating swap. It is half of one. Two selectors in that
census are declared twice, so the collection holds 61 entries across 60 distinct
selectors and a set alone silently loses the duplicate.

The set catches the swap; the count catches a duplicate appearing or vanishing.
Either alone leaves a hole nobody looks for again once the first one is closed.

Found by the integrator for PR #364, which implemented both rather than the set
this file had recommended.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Signed-off-by: Jared Scott <jared.scott@variable.team>
gcko and others added 2 commits September 18, 2026 01:53
…does

DRC-4589 caught the paragraph claiming more than the code. It led with
"resolve both through the cascade and compare them" and then listed two
classes under it, but only one resolves: `AnAbsenceNeverRendersLargerThanItsValueTest`
compares the size each rule declares for itself, deliberately, and its own
docstring says so in as many words. A paragraph warning that a rule count is
not a rendered colour should not itself describe a test by what the test
beside it does.

The two are now described separately, by what each answers. The cascade one
holds the absences raised to the sentence tier. The declared-size one holds
the pairs that are not raised, and fails loudly when a pair stops declaring a
size rather than letting it inherit quietly.

The delegation pair stays in the stamp class rather than moving to the
declared-size class the taxonomy would put it in. DRC-4589 argued against its
own placement and then gave the better reason to keep it: the test exists
because the stamp assertions do not cover that pair, and a reader who opens
that class and finds only stamp assertions is one step from concluding they
suffice, which is what both of us concluded before measuring. Filing it
correctly would hide the gap it was written to expose.

Prose only. No byte pin moves.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Signed-off-by: Jared Scott <jared.scott@variable.team>
`AnAbsenceNeverRendersLargerThanItsValueTest` is where DELEGATION's figure and
the withheld string that replaces it belong by type: separate selectors,
neither raised, both declaring literal sizes. It is not there, and the reason
is only recorded in the class that does hold it, so a reader arriving by type
finds an absence with no explanation.

The pointer names the pair, says where it lives, and gives the reason: it sits
beside the stamp assertions precisely to show they are not sufficient, because
the stamp constrains only the absent variant and says nothing about the base
rule it sits on. Measured at 40px against the 32px figure, where every stamp
assertion stays green.

Both readers are served now. The one who goes looking by type finds the
pointer; the one who opens the stamp class and might conclude those assertions
are enough finds the counter-example in front of them. That conclusion is not
hypothetical: DRC-4589 and I both reached it before either of us measured.

Docstring only. No assertion changes and no byte pin moves.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Signed-off-by: Jared Scott <jared.scott@variable.team>

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

Several UI state paths remain inconsistent, and the byte/count evidence conflicts with the reviewed assertions.

Get a fresh assessment by requesting another Copilot review.

Pull request overview

Integrates the remaining Cargento cockpit UI/UX changes across styling, layout, state cues, caveats, and timeline filtering.

Changes:

  • Adds role-based inks, absence markers, typography guards, and updated byte pins.
  • Reworks cockpit tabs, scope cards, Console ordering, Held to, and steer controls.
  • Updates design documentation and user-facing skill guidance.

Live browser walk was not completed in this environment.

File summaries
File Description
docs/design-runtime-architecture.md Updates module ownership and cockpit ordering.
docs/design-reader-state.md Documents timeline and disclosure persistence.
docs/design-next-ui.md Records UI, typography, ink, and caveat decisions.
CONTRIBUTING.md Adds ink-register guidance.
cargento/skills/cargento/SKILL.md Updates user-facing cockpit behavior.
cargento/skills/cargento/cargento_runtime/web/styles.css Implements visual roles, markers, and layout changes.
cargento/skills/cargento/cargento_runtime/web/project.js Adds timeline mode persistence and disclosures.
cargento/skills/cargento/cargento_runtime/web/next-delegation.js Adds absence stamps and relocates guardrails.
cargento/skills/cargento/cargento_runtime/web/next-controls.js Moves and clarifies the steer composer.
cargento/skills/cargento/cargento_runtime/web/next-cockpit.js Reworks cockpit rendering, cues, caveats, Console, and Held to.
cargento/skills/cargento/tests/test_next_page.py Adds typography/ink contracts and updates pins.
cargento/skills/cargento/tests/test_next_flag.py Updates assembled-page pin.
cargento/skills/cargento/tests/test_next_delegation.py Tests absence rendering.
cargento/skills/cargento/tests/test_next_controls.py Updates steer caveat assertions.
cargento/skills/cargento/tests/test_focus.py Updates assembled digest.
Review details
  • Files reviewed: 15/16 changed files
  • Comments generated: 8
  • Review effort level: Lite

💡 Configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread cargento/skills/cargento/cargento_runtime/web/project.js Outdated
Comment thread cargento/skills/cargento/cargento_runtime/web/next-cockpit.js
Comment thread cargento/skills/cargento/cargento_runtime/web/next-cockpit.js Outdated
Comment thread cargento/skills/cargento/cargento_runtime/web/next-cockpit.js Outdated
Comment thread cargento/skills/cargento/cargento_runtime/web/next-cockpit.js
Comment thread cargento/skills/cargento/tests/test_next_page.py Outdated
Comment thread cargento/skills/cargento/tests/test_next_page.py
Comment thread cargento/skills/cargento/tests/test_next_page.py Outdated
gcko and others added 2 commits September 18, 2026 06:04
…t claims

Six review findings triaged by execution rather than by reading. Two were
refuted on their premise and four were real; the sweep that replaces the
weakest criterion then found a fifth instance nobody had looked for.

Refuted, no change made:

- projectAction's graph-mode ternary drops `decisions`, but the function has
  no caller and the `data-calm` attribute it serves has no dispatcher in the
  bundle. The live Decisions button is rewritten to
  `data-next-cockpit-action="graph-mode"` and passes its argument through the
  validating projectSetGraphMode. Executed against the rendered cockpit:
  decisions -> all -> decisions, heading RECORDED DECISIONS.
- The unknown HOW IT LANDED card renders a stated absence, not a blank slot.
  nextObservedPair falls back to the reason, so endText and claimText are
  never empty, and the pair separates on the `--absent` selector. The
  `data-next-absent` stamp is the figure-slot form of that ruling, which a
  sentence card is not.

Fixed:

- The re-entry disclosure said "Why a raise is unavailable" one line under
  "Its terminal can be raised". The label now travels with its branch.
- The Decisions tab cue reported a context entry that had not arrived as
  "decisions not published" while the panel beside it said "Loading semantic
  context". `pending` existed and was reached only at session scope.
- The Console setup summary told a reader whose server was started with both
  capabilities on that the terminal bridge and the observer model were off,
  because an unread capability collapsed into false. Both are three-state now.
  Measured on a fixture serving an enabled model and a registered terminal:
  unread read false/false where settled read true/true, and the terminal
  section sat inside the setup disclosure on that render.
- Two sentences claimed the observed record is above them. DRC-4594 moved it
  last, and both are false by construction rather than on one fixture.

Oracles, replacing four criteria that were verified over lists:

- HeldToPositionalSentencesTest sweeps every positional sentence the bundle
  emits, reads the direction each one states, and compares rendered indices on
  the correct side. It found a third instance on its first run: the two settle
  cues say "the question above", and nextCockpitConflict draws them inside its
  own header, above the question. Both are corrected. A positional word is
  kept and required to be true rather than deleted, because a sweep with
  nothing to find passes forever.
- The Held to lede is now measured against every absence element the tab
  renders, derived by the same convention, rather than four listed strings
  passed through a filter that dropped its own misses.
- "two axes, read separately" is checked over every .py, .js, .css and .html
  under cargento_runtime rather than two files.
- The caveat-survival set is derived from the merge base's own reading-why
  paragraphs, split into sentences because the tiering splits a paragraph. The
  revision and the extraction are recorded, and a test re-derives the list
  wherever the base blob is reachable.

The absence register guard matched 8 of the 14 rules that carry an absence
marker: four spell it with a single hyphen and all four spelled their ink
var(--ink3) directly, so repointing --ink-absence would have left them behind.
The sweep derives its subject now, the four rules are repointed, and two
grouped selectors were split because each shared a rule with something that is
not an absence.

Byte pins regenerated from the assets. Full suite green: 3636 dashboard tests
and 515 script tests, with coverage at 86.8% against a floor of 73.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Signed-off-by: Jared Scott <jared.scott@variable.team>
The three-state capability read in 8b9b57a keyed off the `loading` flag
rather than the state beside it, and that is wrong for the arm that matters.
`projectTerminalLookup` marks a REGISTERED terminal
`{state:"registered", loading:true}` while it re-checks, and it re-checks
whenever the payload revision advances -- which is every poll. A flag-based
read therefore reported `null` on every poll of a working console, flipped the
summary to "terminal bridge not read yet" and moved the live terminal section
into the setup disclosure it had just left.

Measured before and after: settled `{terminal:true}`, one revision later
`{terminal:null}` with the terminal inside the disclosure; now `{terminal:true}`
across the refresh with the section still outside it. `state` is the tri-state
on its own -- `loading` only before any answer, then `registered` or
`unavailable` -- so the flag plays no part in the decision.

Found by re-reading the diff rather than by a suite, which is why it now has
one. `ConsoleSetupNeverCallsAnUnreadCapabilityOffTest` pins all three states,
asserts the refresh really is in flight before comparing, and was confirmed to
fail on three mutants: the `loading` read this replaces, the original
`Boolean()` pair, and a version that reports an unavailable terminal as unknown
rather than off.

Byte pins regenerated. Full suite green: 3639 dashboard tests, 515 script
tests, coverage 86.8%.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Signed-off-by: Jared Scott <jared.scott@variable.team>
gcko added a commit that referenced this pull request Sep 17, 2026
Two rules from the review of PR #364.

A defect needing two projects to be visible cannot be found by mutating a
one-project fixture at any width or with any assertion. The milestone's worst
defect was a localStorage key that collapsed across projects; both oracles
passed and one seeded the collided key as its expected value. Ask whether the
fixture holds two of the thing a criterion quantifies over before asking whether
the assertion is strong enough.

And a survivor has three explanations rather than two: the oracle is blind, the
substitution never applied, or the run never loaded the test that kills it.
Three findings were withdrawn on the third cause after re-running against the
full selection -- the mirror of the no-op case recorded above.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Signed-off-by: Jared Scott <jared.scott@variable.team>
gcko and others added 4 commits September 18, 2026 06:54
F1. The cue keyed off `entry.data`; the panel keyed off `entry.data` AND
`entry.error`, and the poll's `.catch()` is the only writer of `error`. So a
first fetch that FAILED left the panel saying "Semantic context unavailable."
beside a cue reporting `pending`, glossed "decisions not loaded yet" -- a
completed, failed read described as still in flight. The cue's own comment
asserted the opposite.

`nextCockpitTabCue` does not exist on origin/main; a54451d introduced it in
this pull request, so this is a regression created here rather than one found
in passing, and it is fixed rather than filed.

Reproduced at BOTH scopes, which is one more than was reported: the focused arm
had the same blind spot, and it differed a second way as well -- the panel also
requires the PROJECT entry when a session is focused and the cue did not.

Two conditions that must agree is the shape that produced both, so there is now
one `nextCockpitContextRead` and neither caller re-derives the answer. The cue
also takes its facts from that read at both scopes instead of from the
`observation` argument. The two were the same object, measured `===`, so no
board changes; what goes is the second read that let state and facts drift.

A failed read gets its own cue state rather than borrowing one. It is not
`pending`, which claims a read is still coming, and not `unobserved`, which
claims nothing was published. The mark is the em dash this sheet already uses
for a slot with no figure in it.

The variant needed a rule of its own, and the obvious test does not prove it
has one: pinning the emitted class name passes whether or not the sheet styles
it, and measured, deleting the rule left the whole cockpit module green. Only
the byte pins went red, and those fire on any stylesheet edit.
`EveryTabCueVariantIsColouredByItsOwnRuleTest` derives the variants from the
producer and resolves each through the cascade against the unstyled cue, so a
state added with no rule reds instead of rendering an absence in the ink a real
figure gets.

Seven mutants run, all seven fail the suite: the cue ignoring `error`, the mark
collapsing into `unobserved`, the panel dropping its failed arm, each of the
two variant rules deleted, and a newly emitted variant with no rule. Two
earlier mutants that appeared to survive were re-run after confirming the
substitution had applied; both then failed.

Byte pins regenerated. Full suite green: 3640 dashboard tests, 515 script
tests, coverage 86.8%.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Signed-off-by: Jared Scott <jared.scott@variable.team>
…racles

The blocker and every finding from the four verdicts, in one commit.

M1. `projectSetGraphMode` keyed on the session, and `projectQuerySession` is
"" at project scope for EVERY project, so one entry held every project's
choice -- and this branch newly mirrors that map to `localStorage`, which
turned a per-tab quirk into a persisted one. The scope is now the session when
one is focused and the project otherwise, composed by ONE function the write
and the read both call, because namespacing only the write passes half the
criterion. `projectDisclosure` already composed that pair; this copies it
rather than inventing a shape. The mirror is kept: it is a shipped capability
and the criterion asks for the key by name.

The fixture is the oracle here. Both of that issue's tests passed on a
one-project board and one SEEDED the collided key as its expected value, so no
stronger assertion at any width could have found this -- only a second project.
The new test presses on one, opens the other untouched, presses there, and
returns, because a fix that resolves the fallback from the wrong scope passes
the first half alone.

The context map. `nextCockpitContexts` has five reachable shapes, three
writers, and only one writes `error`. The tab cue this branch adds keyed off
`.data` alone and called a finished, failed read "decisions not loaded yet"
beside a panel already saying "Semantic context unavailable." Both surfaces now
resolve one classification, so they cannot describe a state differently. The
fifth shape is the one neither independent enumeration had and the comparison
between them found: the `.catch()` MERGES, carrying previous data into the new
object, so a failure over loaded data is invisible to `.data`. It keeps its
rows and claims nothing about staleness; whether the board should SAY the rows
are stale is DRC-4613, not a sentence invented inside a fix commit. The
comment that claimed the two "read one object under two names" -- true of the
object, false of the field sets -- is replaced by what the function does.

`projectTerminalLookup` preserved `registered` across a re-check and reset
`unavailable` to `loading`, so a default bridge-off console flipped to "not
read yet" on every poll. That is the third instance of this shape and it was
inside the repair for the first. The pinning test drives it the way production
does and samples on the render that STARTS the re-check: a sample taken four
microtasks later passes on the broken code, which is why the first version of
this test did.

`.next-guardrail-copy small` rendered at 12.5px inside the compliant set, being
declared at 15px in one grouped rule and 12.5px by the next at equal
specificity. Taken structurally: the compliant set is resolved on ELEMENTS, and
the sheet no longer declares a size it immediately overrides. The straddle
census is pinned because straddling is the precondition even where it is
benign, and the shipped defect was a member of it.

`docs/design-next-ui.md` said a per-rule census "will understate the set". It
can also overstate, by a different mechanism -- two rules at equal specificity
rather than three composing -- and both are now written down, with the limit of
the new guard stated rather than implied.

Oracles, each with its own falsifier run: DRC-4597 AC-2's count is exercised
across group sizes, since every fixture held exactly two sessions and a literal
satisfied the derivation; DRC-4593 AC-4 asserts registers as well as pairs, and
deliberately not three hexes, because label and caption share `--ink3` by a
stated ruling; DRC-4595 AC-1 holds every part of the setup block behind the
rail, not just the disclosure; DRC-4591 AC-7 runs on every tiered tab including
the `pc-` surface that issue added; DRC-4591 AC-3 requires the disclosure to
hide something; DRC-4594 AC-5 exercises all six branch combinations.

Not closed, and not claimed: the element-resolved guard does NOT close
DRC-4595's AC-5 hole. A rule reaching an element through ancestors the tier
selector never names is still invisible, because the path is built from the
selector rather than from the page. Widening it by hypothesising every DOM a
rule could match was measured and abandoned at 8,873 false positives.

All thirteen pins re-derived from the assets after the last edit. Suites green:
3652 dashboard, 515 scripts, coverage 86.8% against a floor of 73.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Signed-off-by: Jared Scott <jared.scott@variable.team>
…sweep

Four corrections to the previous commit, three of which make a repair stronger
rather than wider.

The mode key now carries the project AND the session, joined on a separator no
label can contain. Keying by the session alone was the collision; keying by the
project alone silently destroys the per-session distinction the resolver reads
at session scope, and that mutant passed every test the previous commit added.
It has one of its own now: three scopes in one project, each pressed to a
different mode, each read back after visiting the others.

`projectLoadGraphModes` validated values and not key shapes. A browser that has
already run this branch holds every project's choice under one empty key, so a
scheme where that key parsed as a real project would hand the old collision
straight to the new one. Unparseable shapes are dropped once at load rather
than defended against at each read site, and the separator is what makes the
shape decidable.

AC-4's storage test is rewritten against the two-project fixture rather than
patched. It seeded the collided key and asserted the first render took it --
the defect written down as the expected result -- and on a one-project board
that key reads as "project scope" rather than as "every project", so changing
the seed alone would have left a one-project fixture asserting a per-project
property.

AC-7 is derived over every `web/*.js` instead of rendered on a list of tabs.
Adding the Console surface to a fixture was the weaker repair: a second fixture
still cannot fail on the surface nobody thought to add, which is the argument
`js_literals` makes about itself and the reason AC-4's retired-span check walks
the runtime rather than naming files. The sweep asserts its file count, and its
one blind spot -- a template literal split at `${...}`, so an href composed
through a hole is out of reach -- is in the docstring rather than implied.

Four mutants, each with the substitution proved applied: the session-only key,
the project-only key, a read that resolves from the other scope, and a load
without the shape check. A `docs/` href planted in three different bundle files
reds the citation sweep from each.

What is NOT claimed. The cue and the panel agree across all five context
states and neither calls a finished read pending; the stale-plus-error state
renders its last known rows by the ruling in DRC-4613 and is separately
classified so that issue can act on it. They agree through one classifier, so
a future defect there is one condition wrong rather than two readers drifting
-- but agreement is not coverage, and DRC-4613 is open.

Still open and not closed by this commit: DRC-4595's AC-5 hole, since the
element-resolved guard builds its path from the selector rather than the page;
and DRC-4597's AC-6 live measurement, which needs a viewport this desk would
not give.

Pins re-derived from the assets after the last edit. Suites green: 3654
dashboard, 515 scripts, coverage 86.8%.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Signed-off-by: Jared Scott <jared.scott@variable.team>
…e six

Five items, all test and doc; no runtime file changes and no pin moves.

AC-3 had two halves and checked one. That the count rows stay OUT of the
disclosure was asserted; that the disclosure has anything IN it was not, so the
criterion's own empty-body falsifier passed. Removing `nextCockpitWhy`'s
empty-body return leaves 3652 behavioural tests green and draws exactly one
`<p class="next-cockpit-reading-why"></p>` in the re-entry block on the default
board -- a reachable arm emitting an empty sentence, seen by nothing. Both
shapes are now rejected across the whole tab.

AC-5's matrix had collapsed a second time, and in my own repair. The capability
stub returned `{content:"tmux"}`; `nextFocusCapability` requires
`typeof meta.getAttribute === "function"`, so it resolved to "" and all six
cases drew the capability-off branch -- one raise sentence, no disclosure, one
of three branches. Every per-case guard passed, because the anchor and both
rows render on every branch, which is why guarding each case cannot see six
copies of one. The stub is now `CockpitHeldReEntryTest.FOCUS_ON`'s, which
answers `getAttribute` as the page reads it, and the six are asserted to DIFFER:
three distinct raise claims, two distinct re-entry sentences, six distinct
pairs, and the disclosure present on exactly the two branches that have a limit
to explain.

`--ink-caption` doubles onto `--ink3` and `design-next-ui.md` recorded only
`--ink-label` and `--ink-absence`. The ruling cited for declining a
three-distinct-hexes assertion was therefore one role short of covering it.
Three of the four register names land on one hex; only `--ink-value` has an ink
to itself, and that is now written where a reader can re-read it rather than
rediscover it from a failing test.

`_below_floor` swallowed `UnsupportedSelectorError`. It now returns the skip
count and the test asserts it is zero -- 77 swept, 77 resolved, 0 skipped.
Recorded honestly as a TRIPWIRE and not a live guard: putting a sibling
combinator on a tier selector does not produce a skip, because `resolve` calls
`matches` over every rule and lets the error out, so the sweep raises before
the branch is reached. `_steps` is shared, so anything `path_for` refuses,
`matches` refuses too. The branch opens only if the resolver is later changed
to skip rules it cannot express, which is precisely the change that would open
the hole.

Mutants: removing the empty-body return reds AC-3; restoring the broken
capability stub reds AC-5 with `6 != 2`; moving the anchor last reds all six
branches. The skip counter has no falsifier and the docstring says why rather
than implying one.

Suites green: 3654 dashboard, 515 scripts, coverage 86.8%. Byte pins re-derived
and unchanged, since nothing in this commit touches an asset.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Signed-off-by: Jared Scott <jared.scott@variable.team>
@gcko
gcko merged commit f456175 into main Sep 18, 2026
12 checks passed
@gcko
gcko deleted the spacedock-ensign/ui-integration branch September 18, 2026 02:22
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants