diff --git a/docs/plans/ci-two-tier-placement-redesign.md b/docs/plans/ci-two-tier-placement-redesign.md index 4d7fbf2a7da..3a69dacf6d2 100644 --- a/docs/plans/ci-two-tier-placement-redesign.md +++ b/docs/plans/ci-two-tier-placement-redesign.md @@ -82,8 +82,10 @@ regen cold control, divergence counting. A red is loud and blocks/reverts on mai ## 5. Dependency graph (exact) -(Logical dependencies only — **delivery is compressed to one redesign PR**, §8; D0 is routed -to the #7129 worker as its own close-out PR and sequenced first.) +(Logical dependencies only — **delivery is compressed to one redesign PR**, §8. The earlier +routing of D0's close-out to the #7129 worker as its own "PR-0" is DISSOLVED by the §8 +delivery restructure: #7162 carries D0's fixes along with everything else; every "PR-0" +mention below is a historical reference to that dissolved split.) ``` D0 #7129 — MERGED (bd5afd6bc3), NOT closed: whole-schedule arming hoist @@ -106,8 +108,13 @@ D1 child-spawn dissolution — LANDED on main (#7122 one-child-per-call, D1b (deferred — §9.6) in-process claims: run_claims_in_process landed with #7129 (cli_run.rs:9580), INACTIVE; activation gated on D0 close-out + its three fixes. NOT in PR-1. -D4 selection-control step deletion — PRECONDITION: the falsifier cadence - must be green first (today it is red 5/5 by crawl-timeout, §10 row 2) +D4 selection-control step deletion — precondition NORMALIZED per §8's + restructure: a main-cadence green is impossible pre-merge by + construction (the falsifier's fix, D0, is inside #7162), so the + deletion receipt is a BRANCH falsifier run post-D0 (add + workflow_dispatch to falsifier.yml if absent); D4 rides #7162 iff + that green run exists at landing. The per-PR audit stays the working + control until #7162 merges. D5 DiffBaseline fix — independent; unblocks stacked-PR selection ``` @@ -325,8 +332,9 @@ widen a budget to absorb it** (§5; the absorbing-fallback rule applied to our o inherits cross-owner `cli_run.rs` surgery — the drag the single-PR directive exists to avoid. (Weight shifted 2026-07-24 by the implementer's read: D1b absorbs only 2 of the 6 residual children and does not remove their per-entry resolves — §5.) -7. **D4 delivery shape** — resolved per review finding 5: fast-follow micro-PR after the - first green falsifier cadence post-PR-0; PR-1 does not wait on it (§8). +7. **D4 delivery shape** — SUPERSEDED by §8's delivery restructure (this row's fast-follow + micro-PR shape predates it): D4 rides #7162 iff a green branch-falsifier run exists at + landing; no separate micro-PR, no PR-0. Kept as the record of the earlier resolution. 8. **Batch-3 budget × hub-file selection — PR-1 will hit this wall; decide the raise now.** Live receipt (run 30063529282, 2026-07-24, srv4-05): a one-token edit to `ci_spec.dag` + `design_document.dag` selected the full corpus — 2,315 witnesses, **all PASS**, RSS 8.9 GB @@ -349,47 +357,66 @@ widen a budget to absorb it** (§5; the absorbing-fallback rule applied to our o `budget = fixed_overhead + selected_units × per_unit_rate[host_class]`, with the per-unit rate the operator-signed constant (it is the regression dial the humans already compute by hand in every one of these triages) and the 55-min step timeout staying the absolute - backstop. **Operator ruling (2026-07-24): no interim raise — the clamp model lands with - PR-1 atomically.** The signed shape is TWO constants per unit class, with different jobs: + backstop. **Tree reconciliation (post-merge review, 2026-07-24): the stopgap raise LANDED + independently via #7137** — `gunbc_ci_floor_batch_wall_budget_seconds[2]` went 1320 → 1440 + → 1680, both raises operator-signed on the carrier's note with run ids (operator's word on + the second: "we're redoing it completely anyway"). The "no interim raise" ruling recorded + here is therefore normalized to what it means against the tree: **no further raises** — + the clamp model lands with PR-1 atomically and the hand-set rows delete then. The signed + shape is TWO constants per unit class, with different jobs: a **hard max per unit** (witness: 5s — the existing fast-lane authority, unchanged, typed refusal per witness) and an **expected average** as the aggregate coefficient (witness: 1s). Batch clamp = overhead + units × average (full corpus ≈ 300s + 2,316 × 1s ≈ 44 min, under the 55-min cap, ~1.6× the honest healthy wall — catches runaway quickly, never refuses legitimate work); the per-unit max protects the tail the average can't see. + **Constant basis — record it on the signed row (review addition, 2026-07-24):** the 1s + average is denominated in effective WALL per unit at the floor's current worker width on + fleet-class hosts (observed full-corpus rate ~0.58–0.70 s/witness) — host speed AND + parallel width are folded into the constant; the earlier `rate[host_class]` axis was + dropped silently when the two-constant shape was signed. Fine under today's ~1.6× + headroom, but a width or fleet change re-prices the constant, so the signed row must name + its basis (host class × width) and a change on either axis is a deliberate re-sign, never + a rediscovered fleet-wide-red episode. **Creep has no durable home yet, give it one:** + the clamp catches runaway (≥ ~1.6×); the regression dial (s/unit) renders in the + observation heartbeat and grounds AttentionLevel — visible only while a human watches. + The gauntlet cadence therefore appends a per-run s/unit rate receipt row (host, units, + wall) so cost creep is counted and trendable — a Notable that recurs across cadence + windows becomes a filed row, not a memory. Changing either constant requires an appended operator-signed line (the existing `budget_note` discipline re-pointed at these constants; the unit count needs no signature — the schedule computes it). The hand-set `gunbc_ci_floor_batch_wall_budget_seconds` rows delete. These clamps are declared interim mechanics: the structural wall is the complexity lens (§8 — every clamp is `cost ≤ a + b·n`, the linearity assertion the lens makes structurally at compile time), and the clamp demotes to host-pathology backstop when the - lens goes Blocking. + lens goes Blocking — demotes, never deletes: the lens asserts structural linearity, not + constants or host pathology, so the clamp stays the permanent backstop. Sign-offs recorded here with name + date once reviewed. ## 10. Issue-closure checklist (operator-requested 2026-07-24; worked through by execution before review) Every issue from the 2026-07-23/24 CI discussions, keyed to its closing mechanism and the -verification actually performed. Statuses are honest: LANDED (verified), PLANNED (in PR-0/PR-1), -BLOCKED (named precondition), OUT-OF-SCOPE (named owner elsewhere — listed so nothing silently -drops). +verification actually performed. Statuses are honest: LANDED (verified), PLANNED (in #7162 — +the dissolved PR-0/PR-1 split's single successor, §8), BLOCKED (named precondition), +OUT-OF-SCOPE (named owner elsewhere — listed so nothing silently drops). | # | Issue | Mechanism | Status · verified how (2026-07-24) | |---|---|---|---| | 1 | 26 cold child processes (~23 min, 47.5% of floor) | #7122 one-child-per-call + #7128 `cheap_gate_pool` union (K-chunked, cap 16/child) | **LANDED** — 6 pooled children counted in run 30052571652's ci log (readiness-probe quadruples at 6 distinct timestamps); floor 48.4m → 37m58s. Residue 6 = 1 union + 4 ingest-overlay (by construction, W3-only) + 1 reads-class (D1b-absorbable; D1b removes spawns, not per-entry resolves) | -| 2 | Selection-control audit (4m08s every run) | D4 deletion; falsifier cadence = surviving control | **BLOCKED — do not delete yet**: falsifier is red 5/5 scheduled runs (latest 30044928186: crawl regime, cgroup pinned 16.1G, swap 32G saturated, one module typecheck 3,171s, killed at the 170m cap). Today the per-PR audit is the only WORKING selection control; deleting it now would be the §5 fail-open. Expected unblock: #7129 eviction + PR-0's P1 fixes let the cold walk finish; D4 ships as a fast-follow micro-PR citing the first green cadence run (§9.7) | -| 3 | Retention truth (compile-clean graph pinned · all-hit keys unregistered · arming≠loader closure) | PR-0, #7129 worker | **PLANNED/ROUTED** — empty-list fail-open + missing arming CONFIRMED by direct read of `run_claims_in_process` (cli_run.rs:9580); blocker 1 is also the falsifier-crawl mechanism (row 2) and part of the 9.2GB floor | -| 4 | `run_claims_in_process` fail-open (empty→true) | D1b activation fixes, #7129 worker | **PLANNED/ROUTED** — must NOT activate in PR-1 (§9.6) | +| 2 | Selection-control audit (4m08s every run) | D4 deletion; falsifier cadence = surviving control | **PLANNED IN #7162** (normalized 2026-07-24 per §8; this row's earlier BLOCKED/fast-follow shape is superseded): falsifier red 5/5 (latest 30044928186: crawl regime, cgroup pinned 16.1G, swap saturated, one module typecheck 3,171s, killed at the 170m cap) is exactly WHY a main-cadence green is impossible pre-merge — its fix (D0) is inside #7162. Deletion receipt = a green BRANCH falsifier run post-D0; the per-PR audit stays the only working selection control until #7162 merges, so it is not deleted before then | +| 3 | Retention truth (compile-clean graph pinned · all-hit keys unregistered · arming≠loader closure) | **#7162** (the PR-0/#7129-worker routing dissolved by §8's restructure) | **PLANNED/ROUTED** — empty-list fail-open + missing arming CONFIRMED by direct read of `run_claims_in_process` (cli_run.rs:9580); blocker 1 is also the falsifier-crawl mechanism (row 2) and part of the 9.2GB floor | +| 4 | `run_claims_in_process` fail-open (empty→true) | D1b activation fixes — deferred (§9.6), activation only after #7162 lands D0's fixes | **PLANNED/ROUTED** — must NOT activate in #7162 (§9.6) | | 5 | Discovery ~643s resolve (eval 18.4s) | D2 fresh attribution → store-econ class | **OPEN, probe-owned** — stale "fixpoint" lever retired; no fix dispatched before attribution | | 6 | Store teardown ~2.5m (paid-twice Drop) | D2 ledger row; store-econ / ROADMAP ① | **OUT-OF-SCOPE for PR-1**, named owner | | 7 | Whole-tree startup index every process (#6848 census heads) | census re-grounding on SymbolIndex (namespace lane) | **OUT-OF-SCOPE**, mitigated: 27→7 payments/run via pooling | -| 8 | DiffBaseline `origin/main` hardcode (stacked-PR mis-selection) | D5 in PR-1 | **PLANNED** — brief already delivered | +| 8 | DiffBaseline `origin/main` hardcode (stacked-PR mis-selection) | D5 — superseded by #7146's `gunbc.diff_baseline` (§11) | **LANDED (#7146, on main)** — typed `CiDiffEvent` resolver with AFFECTED-SET REFUSAL, 11 witnesses; #7162 carries only the parked miscite fix + the `Env.Get` mock as D5's named second part (§8) | | 9 | Trivial-diff floor tax | rows 1+2 combined | **PARTIALLY LANDED** — children pooled; audit minutes pend row 2; sheet re-anchors Before at probe time | -| 10 | 9.2GB resident floor | PR-0 blocker-1 (compile-clean unpin) + census (row 7) + walk_memo (named follow-on) | **SPLIT** — largest slice PLANNED (PR-0); residuals named, owned elsewhere | -| 11 | 2,652 `UnlistedImportUse` fork (CLI vs floor compile-clean policy) | needs an owner — namespace-lane promotion staging vs fail-open fork, undetermined | **UNOWNED — flagged to operator** (not this plan's scope; recorded so it cannot drop) | +| 10 | 9.2GB resident floor | D0 blocker-1 (compile-clean unpin, in #7162) + census (row 7) + walk_memo (named follow-on) | **SPLIT** — largest slice PLANNED (#7162); residuals named, owned elsewhere | +| 11 | 2,652 `UnlistedImportUse` fork (CLI vs floor compile-clean policy) | needs an owner — namespace-lane promotion staging vs fail-open fork, undetermined | **UNOWNED — flagged to operator** (not this plan's scope; recorded so it cannot drop; **re-flagged by the 2026-07-24 post-merge review — still unowned**) | | 12 | `.rs`-diff whole-tree widening | deliberate policy (§3.3, §9.5) | **UNCHANGED BY DESIGN** | | 13 | Serial chain ~10m (build 2.4 + regen 5.3 + deploy 2.2) | regen scoping exists; further work unpriced | **OUT-OF-SCOPE**, named residual | | 14 | 5s rule needs a crisp definition | already modeled: the fast-lane law (thread-CPU, typed over-budget refusal) | **EXISTS** — §9.1 reuses the threshold + refusal shape; the per-GATE quantity is the probe's to define (§9.1 caveat) | -| 15 | Batch-3 budget under the honest full-corpus wall — fleet-wide, most PR runs red | §9.8 — interim: one signed raise to ~1680s on main; durable: re-denominated budget (overhead + units × rate[host]) in PR-1 | **OPEN, escalated** — 7 walls (1344–1629s) on 5 branches, all witnesses passing, main 12/12 green: no regression, mis-denominated budget | +| 15 | Batch-3 budget under the honest full-corpus wall — fleet-wide, most PR runs red | §9.8 — stopgap raise + durable re-denominated clamp model in #7162 | **INTERIM LANDED (#7137)** — batch-3 row raised 1320 → 1440 → 1680, both raises operator-signed on the carrier note with run ids; the diagnosis stands (7 walls 1344–1629s on 5 branches, all witnesses passing: no regression, mis-denominated budget); durable clamp model rides #7162, hand-set rows delete then | ## 11. D5 — the DiffBaseline brief (in-tree copy) — **SUPERSEDED 2026-07-24** diff --git a/docs/plans/progress-observation-design.md b/docs/plans/progress-observation-design.md index 60bf05600d7..abe600f87e5 100644 --- a/docs/plans/progress-observation-design.md +++ b/docs/plans/progress-observation-design.md @@ -47,10 +47,16 @@ One event vocabulary, emitted by the process, consumed by every renderer: - **`AttentionLevel` is derived, never chosen per site:** `Ambient | Notable (threshold crossing) | Anomaly (refusal, divergence, orphaned begin)`. Event-class → presentation is a **total assignment** — censused and walled like unthemed colors. -- **One glyph/material authority:** status → glyph (`⟳ ✓ ✗ ⛔ ⏳ ⏭` + the reward-animal rows - for `Final`) → register color role, as data rows. Terminal ANSI materials are the register's - roles realized for one more surface; the reward pattern (random animal on terminal success) - lands as rows in this table, not a hardcoded function. +- **One glyph/material authority, TIERED:** status → glyph → register color role, as data + rows, where each row carries three realizations — an **emoji tier** (✅ ❌ 🚫 ⏳ ⏭️ 🕐, the + §6b tone ruling's set), a **unicode tier** (`✓ ✗ ⛔ ◷ ⊘`), and an **ascii tier** (textual + tags) — so color and emoji are never the only channel and pipes degrade losslessly. (The + earlier draft listed the unicode set here and the emoji set in §6b as if they competed; + they are two tiers of the ONE authority — the landed shape in + `dag/extdeps/render/glyphs.dag`.) Terminal ANSI materials are the register's roles + realized for one more surface; the reward pattern (animal on terminal success) lands as + rows in this table, not a hardcoded function — selection is deterministic from the run's + identity so the stream stays replayable. ### The five laws (data rows, operator-signed 2026-07-23) @@ -59,7 +65,8 @@ One event vocabulary, emitted by the process, consumed by every renderer: converts quiet-past-budget into a typed line (this also closes the batch-wall hang gap — a batch that never completes currently rides silently to the step cap). 2. **The heartbeat carries identity, not just vitals.** The per-minute telemetry line gains - the current activity: `phase= entry= module= k/n`. No phase may exceed T seconds without a + the current activity — phase, entry, module, k-of-n — rendered per the §6b tone contract + (a plain sentence, identity first, vitals suffix). No phase may exceed T seconds without a line naming its current subject. 3. **Attention escalates by dwell time, recursively.** A subject quiet past T surfaces its current child; past 2T the grandchild; recursively to the leaf. Same tree, same events — @@ -84,25 +91,35 @@ Append-only; no repaint. The reference implementation's biggest gap is here (its has no heartbeat, no escalation, no contention — the quiet-window problem verbatim), so this renderer is where every law must be first-class. +(Trace normalized 2026-07-24 to the §6b rulings it predated — plain sentences, emoji-tier +glyphs, 🕐 pulse, clamp arithmetic on the refusal, and the refusal rendered OUTSIDE the +group per the placement law; the earlier draft's trace violated its own RED by putting the +⛔ line inside `::group::`.) + ``` -::group::batch 3 — discovery (602 entries) -→ batch 3: discovery begin (602 entries, 2314 witnesses) -✓ entry effect_reach_test.dag (14 witnesses, 230ms) +::group::batch 3 — witness discovery (602 entries, 2,314 witness units) +→ batch 3: witness discovery begins — 602 entries, 2,314 witness units selected by this diff +✅ entry effect_reach_test.dag — 14 witnesses, 230ms … [collapsed: 213 green entries] -♥ t=29m phase=discovery entry=214/602 (emit/rust_binop_emit_test.dag) module=34/61 | rss=16.0G swap=32G(sat) high_events=+90k/min -⏱ entry 214 quiet 120s → typecheck v2.compiler.normalized_tree (34/61) [T: child surfaced] -⏱ normalized_tree quiet 240s → blocked: memory.high reclaim (psi 9.0) [2T: cause surfaced] -⛔ REFUSED batch 3: FLOOR-BATCH-OVER-BUDGET wall_ms=… budget_ms=… (budget row: …) +🕐 still in witness discovery: entry 214 of 602 (emit/rust_binop_emit_test.dag), module 34 of 61 — 823 of 2,314 units done, pace 1.07s per unit (signed average 1s) · rss 16.0 GiB, swap saturated +⏱ entry 214 has been quiet 120s — now inside: typecheck of v2.compiler.normalized_tree (module 34 of 61) [T: child surfaced] +⏱ normalized_tree quiet 240s — blocked on memory.high reclaim (psi 9.0) [2T: cause surfaced] ::endgroup:: -✗ batch 3 — discovery: refused (1 refusal, 213/602 entries complete, 41m12s) +🚫 REFUSED batch 3 — wall 45m03s over its derived clamp 43m34s = 300s overhead + 2,314 units × 1s signed average (implied 1.04 s/unit vs the signed 1s; budget row + both constants named) +❌ batch 3 — witness discovery: refused (1 refusal, 213 of 602 entries complete, 45m03s) ``` Contract: every `Begin` at entry grain+ has a matched outcome line **with duration**; heartbeat -per minute with identity; dwell escalation *appends* deeper-subject lines at T/2T/4T; anomalies -always render immediately and are never inside a collapsed group; a final summary table (the -reference's binary-cache table pattern) plus failed/refused boxes re-rendered at the end so -they survive log truncation; concurrency is attributed (every line carries its subject path or -lives inside its subject's `::group::`). +per minute with identity (plain sentence, identity first, human units — §6b tone); dwell +escalation *appends* deeper-subject lines at T/2T/4T; anomalies always render immediately and +are never inside a collapsed group — on an anomaly the renderer **closes the enclosing group +before emitting the line** (in the landed renderer this is construction, not discipline: +placement derives from attention, so an anomaly inside a collapsed group is unwritable); +derived-clamp refusals render WITH their arithmetic (§6b — units, coefficient, overhead, +actual wall, implied s/unit), so budget triage stops being a hand computation; a final summary +table (the reference's binary-cache table pattern) plus failed/refused boxes re-rendered at +the end so they survive log truncation; concurrency is attributed (every line carries its +subject path or lives inside its subject's `::group::`). ### 3b. Interactive TTY (`gunbc` CLI local runs) @@ -122,7 +139,12 @@ formats do not change** (they have consumers); they become derived views, dissol their own triggers. ### 3d. Dashboard (belt B) — later consumer, same stream; out of scope here, must not be -precluded (the event schema is the wire contract). +precluded (the event schema is the wire contract). One boundary named so it never blurs: +the dashboard composes TWO legitimate sources — the belt's supervisor view (tmux session +liveness, the P2a sessions surface) and this stream's self-report — which can disagree +(belt: running; stream: quiet 120s). Different subjects, not a fork; the composition rule +is a row on the dashboard lane (the workspace UX plan §P2c carries it), never an ad hoc +merge in a renderer. ### 3e. Non-TTY local pipe — the CI contract minus workflow grouping markers. @@ -271,8 +293,6 @@ Same house, different organs — related by shared authority, not by overlap: ## 7. Non-goals -## 7. Non-goals - No external logging framework; no dashboard rebuild; no new telemetry sources in P0–P1 (render only what is already computed — the single exception is plumbing subject identity to the heartbeat); receipt file formats unchanged; the batch-wall in-flight deadline is its own row diff --git a/docs/plans/roadmap-workspace-ux-plan.md b/docs/plans/roadmap-workspace-ux-plan.md index 36e78bd6999..ffa4796c87e 100644 --- a/docs/plans/roadmap-workspace-ux-plan.md +++ b/docs/plans/roadmap-workspace-ux-plan.md @@ -3,6 +3,12 @@ **Status:** DESIGN for operator review (directed 2026-07-24: "I need to be able to actually work out of this soon"). Three pillars, each with exists/missing stated honestly. Authority migrates to carriers as slices land; this doc is the discussion artifact. +**Normalization pass (post-merge review, 2026-07-24):** the doc accreted operator passes in +layers; this pass makes the END-TO-END CONTRACT the single controlling text (earlier layers +that disagreed — the P3b pulse, the P2b status-chip overload, the Sequencing order — are +corrected in place), repoints the dead squash hash, and records the two verified modeling +gaps (session terminal states have no observable source; the tmux read-back carries no +timestamps) so PR-B starts from the honest exists/missing line. ## Thesis @@ -16,9 +22,10 @@ This plan implements them on the workspace. ## Pillar 1 — Readable (largely LANDED 2026-07-24, residue named) -- **Landed** (`2ba4d8f60c`): rows render their derived lead with the full brief behind a - native `
` disclosure (188 blocks, zero JS); superseded strikethrough live (the - raw-text serialization fix); chips no longer overflow; containers centered. +- **Landed** (#7169, merge `b867e9b58` — the branch hash this line first cited was destroyed + by the squash-merge): rows render their derived lead with the full brief behind a native + `
` disclosure (188 blocks, zero JS); superseded strikethrough live (the raw-text + serialization fix); chips no longer overflow; containers centered. - **P1b residue:** the 3 rows whose *lead itself* exceeds 300 chars (authoring nit — tighten the leads on their carriers); superseded/done **sections** collapse by default with counts in the header ("superseded · 14"); done recedes further. Small, no dependencies. @@ -32,16 +39,39 @@ moment** — no GET /sessions surface, no Stop verb, re-dispatch after stop one- (all already filed on `ts-dispatch-redispatch`). - **P2a — sessions surface:** belt B gains `GET /sessions` — (session × node × state × - started × last-activity), projected from the observe half's existing tmux read-back. A - route + projection on the modeled `ServedRoute` machinery; no new architecture. -- **P2b — live rows:** a dispatched node's row shows its session state live - (`dispatched → running → quiet Nm → done/red`), polled from P2a (poll first; push later). - The status chip becomes the live state carrier. Stop + re-dispatch verbs land here - (absorbing the filed row). + started × last-activity). The route is genuinely small (a `RoadmapServeRouteSpec` row + a + handler variant on the fail-closed table in `roadmap_serve.dag`). **Honesty correction + (verified 2026-07-24):** the existing read-back (`dispatch_live_sessions`) parses only + `session_name / node_id / windows / lease_verdict` — `started` and `last-activity` do NOT + exist today. The modeled tmux observation must grow (`#{session_created}` / + `#{session_activity}` format fields + parse rows) on the fail-closed observe path — small, + but real observation work, not a pure projection. +- **P2b — live rows:** a dispatched node's row shows its session state live, polled from + P2a (poll first; push later). Session state renders in its **own session chip** — the + exemplar's line-item layout is the authority; the status chip stays the node-status + carrier (overloading it would conflate node status × session state, the house's own + `Option`/`None` pattern; the earlier "the status chip becomes the live state carrier" + wording is superseded). Stop + re-dispatch verbs land here (absorbing the filed row). + **The terminal-state gap (G1, the load-bearing missing piece):** a session that exits + DISAPPEARS from `tmux ls` — today the belt cannot distinguish done-success from crashed + from reaped, so `done`/`red` have **no observable source** and rendering them from + absence would fabricate (violates *every response true*). Decision required before PR-B, + three options: (a) tmux `remain-on-exit` + dead-pane exit-status read (changes reap + semantics — lingering sessions interact with the re-dispatch debris fix); (b) a + worker-written completion receipt in the worktree, read at observe time (survives session + death, composes with the teardown-owns-worktree fix, gives a typed red with exit code — + **recommended**); (c) ship the honest vocabulary now — terminal state `gone` + the node's + own status — and land exit modeling as its own named row. Whichever lands, absent-session + stays a distinct state from done and from red, never collapsed. - **P2c — progress depth:** when the progress-observation lane lands (its JSONL event stream names the dashboard as renderer N+1 *by contract*), an expanded row shows the live phase/heartbeat — "what is it doing right now" — consuming the same events as the CI log - renderer. Explicit dependency; no second telemetry model may grow here. + renderer. Explicit dependency; no second telemetry model may grow here. **Two-source + composition rule:** the belt's supervisor view (tmux liveness, P2a) and the stream's + self-report (P2c) are different subjects that can legitimately disagree (belt: running; + stream: quiet 120s) — not a fork, but the row UI composes them, and the composition rule + is a row (supervisor liveness gates existence; stream detail fills depth; disagreement + renders as its own named state), never an ad hoc merge. ## Pillar 3 — Tactile (the feel register: defined once at the root, flows everywhere) @@ -49,18 +79,37 @@ The operator's law: principles → design → implementation live in ONE root pl through every surface. That root is the design-register library (`gunbc.design.*`), which already carries scale (dimensions) and theme (color roles). It gains: -- **P3a — `gunbc.design.motion`:** duration tokens (press-acknowledge ~90ms · hover ~150ms · - settle ~250ms · state-blend 12–16 frames, reusing the theme-transition clock), easing - tokens (standard + a settle curve — Nintendo's feel is *overshoot-and-settle*, never - linear), transform vocabulary (press-scale, hover-lift), and the **acknowledgment law**: - every interactive element responds to hover AND press AND every state change, in the same - grammar, from these tokens only — a total assignment, censused and walled exactly like - unthemed colors. Still-until-touched holds: all motion is user-caused; ambient motion - stays unwritable (MotionTrigger has no ambient variant). +- **P3a — `gunbc.design.motion`:** duration tokens — **reuse the existing scale first** + (`respond_fast` 90ms for press-acknowledge, `reveal_deliberate` 160ms for hover — the + earlier "~150ms" was a near-miss mint of a token that already exists), state-blend + 12–16 frames reusing the theme-transition clock. `settle_spring` (~250ms, overshoot + bezier) is a legitimate NEW token but its justification is the **easing axis** — it sits + one row from `restore_gentle` (240ms ease) on duration, so the row records that the mint + is the overshoot curve, not a tenth duration (Nintendo's feel is *overshoot-and-settle*, + never linear). Transform vocabulary (press-scale, hover-lift), and the **acknowledgment + law**: every interactive element responds to hover AND press AND every state change, in + the same grammar, from these tokens only — a total assignment, censused and walled + exactly like unthemed colors. **The law's "every state change" clause is a register law + AMENDMENT, land it as one:** still-until-touched today means motion is user-caused; + the dashboard extends it to *motion only on user touch OR observed state change* (analog + honesty — a real mechanism moves when its state changes), with ambient motion still + unwritable. Mechanically: `MotionTrigger` is `OnHover | OnFocusVisible | OnActive` — + pseudo-classes — while state-change motion triggers on **state classes** (the + `dispatch-*` class flips), a different selector algebra; the new trigger class is the + real modeling work of P3a, landed as a law row + trigger variant with the button as its + first consumer in the same PR (the consumption rule). **The total assignment includes a + `prefers-reduced-motion` projection** (instant flips, zero travel — a register axis + realized once, not a per-site afterthought). - **P3b — dashboard as first consumer:** the dispatch button's full state theater on its - EXISTING class flips (pure CSS, no new JS): press-down acknowledgment, hover lift, - in-flight pulse while `requested`, success settle, refused stop. Disclosure open/close - animates; row hover responds. + EXISTING class flips: press-down acknowledgment, hover lift, `requested` still-and- + distinct (**no pulse** — corrected to match the exemplar; a looping pulse is ambient + motion and the keyframes wall holds), success settle, refused stop. "No new JS" restated + honestly: the theater rides class flips in CSS, but the morph label (`session · spawned`) + is a small emitted-TS text change in `roadmap_component` — no new client *architecture*, + not literally zero JS edits. Disclosure open/close animates **via a named mechanism** + (`::details-content` + `interpolate-size` — Chrome-target, fine for the operator + dashboard; if that's declined, the disclosure stays unanimated rather than growing JS); + row hover responds. - **P3c — every surface consumes the same rows:** the public site and future surfaces take the identical tokens (the register library's lift-not-fork discipline). **Sound is a named later axis** in the same shape — event-class → sound rows, one authority, opt-in @@ -104,7 +153,8 @@ an overshoot easing, and everything AFTER the click. The story: the button becomes the workflow representation the operator asked for. 6. **done** — settle beat + check, then the row's done-recede takes over; button returns to rest, re-armed (the filed re-dispatch one-shot bug is in scope — "perfect lifecycle" - includes the second dispatch). + includes the second dispatch). *Source honesty: `done` (and `red`) render only per the + P2b terminal-state decision (G1) — never derived from session absence.* 7. **refused** — composed refusal: boundary border + a single sharp dip-and-return (scale 0.97 → 1.0, transition-only — no shake keyframes), typed reason surfaced inline. @@ -114,6 +164,17 @@ violation); steady states are still, transitions animate (still-until-touched pr the keyframes wall is NOT breached); every rendered stage is a projection of belt/session fact (every response true). +**The detent table is load-bearing, pin it before any CSS (mechanical constraint, found in +review 2026-07-24):** CSS transitions fire only when a property CHANGES — an edge between +two states holding identical transforms cannot beat without keyframes (walled) or a JS +transient class. So the state rows must assign **each state a distinct stable transform +value** — the detent metaphor made literal (e.g. pressed 0.97 · requested ~0.985 "engaged, +in flight" · terminals 1.0 · each workflow stage its own resting value) — which makes every +edge's travel real and the settle beat *derivable* from the transition + overshoot curve, +never choreographed. The same discipline names the **morph mechanism** up front: the +button→chip morph needs `interpolate-size: allow-keywords` (or fixed measures per state) to +transition auto-width in pure CSS — decide which, don't discover it on landing day. + ### END-TO-END CONTRACT (operator, 2026-07-24: "very clear — no design that is never consumed/finished." Scope = the dispatch button + the workflow representation of ONE line item. Everything else in Pillar 3 is PARKED until this ships.) @@ -129,20 +190,29 @@ consumers at merge is a defect, not a foundation. before release. (register vocabulary + CSS, no belt work) 2. Clicking a real node on the live srv1 dashboard: the button settles-with-overshoot on accept and becomes `session · spawned`. (same PR as 1) -3. The row then shows the live stage — `working`, `quiet Nm`, `done`/`red` — updating +3. The row then shows the live stage — `working`, `quiet Nm`, and the terminal states per + the G1 decision (`done`/`red` only once exit is modeled; `gone` until then) — updating without a page reload, each stage change animated, each steady state still. - (P2a-mini: one GET route projecting the belt's existing observe read-back + client poll) + (P2a-mini: one GET route + the tmux observation growth from P2a + client poll) 4. A planted refusal shows blocked-travel + the typed reason inline. (same PR as 3) 5. After done/stop, clicking dispatches AGAIN, full arc — the re-dispatch one-shot bug is dead. (rides 3) -6. The state-machine totality witness reds when an edge without a timing row is planted, - and the live-page walkthrough (1–5) is recorded as the PR's acceptance receipt. +6. The state-machine totality witness reds when an edge without a timing row is planted; + the keyframes wall extends to the dashboard emission (today's witness covers the + moodboard only); and the live-page walkthrough (1–5) is recorded as the PR's acceptance + receipt. **Walkthrough precondition:** the served-page fingerprint check from the + 2026-07-24 stale-styling incident (deploy refreshed files; the serve path's + artifact/process refresh was unproven) lands with or before PR-B — otherwise the + acceptance demo can show stale assets, the exact failure that motivated the check. File + it as a belt-B row per `ts-intake-discipline`; it is currently prose-only. **The single-line-item workflow representation (checkpoint 3's face):** the row IS the workflow: `status-chip · lead · [session chip: stage · elapsed] · dispatch/stop`. One line, at-rest still, stages as discrete detent positions — a car's gear indicator, not a -progress bar. The expanded brief (details) gains the session's stage history as plain -lines (`spawned 14:02 · working 14:02 · done 14:31`) — projections of belt facts only. +progress bar. `elapsed` is quantized to minutes — a per-second tick is ambient churn, and +quiet-at-arm's-length governs text exactly as it governs motion. The expanded brief +(details) gains the session's stage history as plain lines +(`spawned 14:02 · working 14:02 · done 14:31`) — projections of belt facts only. **Explicitly parked until the exemplar ships:** P3c (other surfaces), sound, section collapse (P1b), any second control's rows. Two PRs total: PR-A = checkpoints 1–2 @@ -150,13 +220,15 @@ collapse (P1b), any second control's rows. Two PRs total: PR-A = checkpoints 1 (sessions read + poll + re-dispatch fix). If PR-B stalls, PR-A alone must still be a strict visible improvement — that is the anti-shelf-ware test. -## Sequencing +## Sequencing — SUPERSEDED by the END-TO-END CONTRACT (kept as the record) -P1b and P3a/P3b have no dependencies — land now. P2a/P2b are belt-B-lane work (the serve -process refresh finding from 2026-07-24 rides along). P2c waits on the observation PR by -declared contract. Order of daily-work value: **P2a/P2b first** (observability is what -"work out of this" needs), P1b + P3b as the polish pass, P3c with the register library's -Phase A. +This section predates the exemplar contract and disagrees with it on two points, so the +contract controls: delivery is **PR-A then PR-B** (button theater first, sessions second — +not "P2a/P2b first"), and **P1b is parked** (not "land now"), along with P3c, sound, and +any second control's rows, until the exemplar ships. What survives from this section: P2c +waits on the observation PR by declared contract, and the serve-refresh finding rides PR-B +as a precondition of its live walkthrough (checkpoint 6). P3c lands with the register +library's Phase A when unparked. ## Non-goals