Skip to content

feat(desktop): workflows family (Plan-023 T-023p-1C-6) - #424

Merged
Sawmonabo merged 488 commits into
developfrom
feat/plan-023-phase-1c-workflows
Sep 6, 2026
Merged

feat(desktop): workflows family (Plan-023 T-023p-1C-6)#424
Sawmonabo merged 488 commits into
developfrom
feat/plan-023-phase-1c-workflows

Conversation

@Sawmonabo

@Sawmonabo Sawmonabo commented Sep 2, 2026

Copy link
Copy Markdown
Owner

Summary

Plan-023 Phase 1C, T-023p-1C-6 — workflows. The workflows rail destination, the definitions browser, the run pane with its phase graph, and the run-list projection over the registered workflow reads, built over the fixture bridge with typed owner slots for the five bodies Plan-017 authors and this task does not.

Based on develop; this diff is the family alone.

What the family renders

The workflows family: the rail destination, both workflow pane chromes, and the
run-list projection over the registered workflow reads — with typed owner slots for
the five bodies Plan-017 authors and this task does not.

The rail destination is now claimed. The rail has had a workflows destination
since the frame shipped and nothing had ever claimed the slot, so pressing it
reached the frame's reserved-slot absence — a true sentence about a definitions
browser this family had in fact already built. The family fills its reserved seat in
console/families.ts, and the browser goes and gets the rows it shows.

Both surfaces read. The definitions browser puts the definition enumeration and
the run pane puts the run read, both through the growth port. A refused read stays a
refusal and never becomes an empty list or an unparked run.

The phase graph. The run's phase sequence is drawn with @xyflow/react, the
library Spec-023 §Console Libraries ADOPTs-with-constraints for this canvas, behind
a memoised dynamic import so it costs the initial bundle nothing. The layered
auto-layout, the node and edge visuals and the stylesheet are own-built, which is
what that row's OWN-BUILD half requires, and every one of the library's custom
properties is set from a Meridian token. The graph is read-only: no connect mode, no
inspector, no drag — the authoring canvas is another plan's body and stays a slot.

The pin is @xyflow/react at exactly 12.11.5, not the 12.11.6 the spec row names.
pnpm-workspace.yaml sets minimumReleaseAge: 1440, and 12.11.6 was published under
24 hours before this branch was cut, so pnpm install refuses it outright; 12.11.5 is
the newest release the guard admits, sits inside the 12.11.x band the row requires,
and is the release that fixed the broken 12.11.4 the row warns about. This is the
precedent the Electron pin already set. No minimumReleaseAgeExclude entry was added
and no zustand alias or override exists.

@xyflow/system is deliberately not a direct dependency, and the lockstep the row
demands is stronger without one. Every @xyflow/react release declares its system
package as an exact version rather than a range — 12.11.5 declares 0.0.81,
and the broken 12.11.4 declared 0.0.80, the very version that lacked the symbol it
imported. So a direct pin on our side could not have prevented that breakage and can
only ever restate what the parent already fixes; what protects the build is the exact
parent pin plus the resolved lockfile under CI's --frozen-lockfile, and the lockfile
does resolve @xyflow/react@12.11.5 → @xyflow/system 0.0.81. A declared dependency
nothing imports is what the dead-code gate exists to reject, and apps/desktop/AGENTS.md
admits no ignoreDependencies exemption for it.

test/console/architecture/workflow-no-graph-library.test.ts is deleted with the
adoption: its entire claim was that this family imports no graph library before the
body that needs one, and that is now false by design.

The three scripted reads are routed. The workflows scenario had scripted the
definition enumeration, the run read and the phase-output read since the scenario
lane, and the fixture growth port answered none of them, so both panes rendered "not
checked" in every fixture build. All three are served now, each with a fallback that
is honest rather than convenient: an enumeration nobody answered is an empty list,
because an enumeration has an empty form and that is a real daemon answer; a run read
and a phase-output read nobody answered are typed refusals, because every member of
those shapes is required and an "empty" run would be five invented facts the operator
controls sit on.

Tiers

The family is pinned in the screenshot tier and the accessibility tier, on the two
surfaces that carry its visual argument — the definitions browser's three scoped
groups and a parked run — in both schemes. Both mount out of the pane registry rather
than by rendering a component directly, so what the tiers photograph is what the
console actually seats.

The pinned-platform skip and the axe runner are hoisted out of the frame tier's files
into modules of their own, unchanged, so a second tier file does not restate either
rule. The accessibility file keeps its planted-violation control: a run that reports
nothing has proved the checker still fails on a known-bad tree.

The family barrel now registers a surface seat beside its pane registrations, and it
reaches the registry type through frame/surface-registry.js rather than the frame's
barrel. That is not a style choice — the frame barrel also exports the composition
root, so a family reaching the seat through it closes
families.ts → workflows → frame → ConsoleRoot → families.ts, which the layering gate
rejects. The reason is written at the import for the next family to read.

What it consumes from the substrate

  • console/core — refusals, the keyed registry, the clock.
  • console/primitivesNothing, RefusalBanner, Chip, WireFigure, Glyph,
    and wire-figures.ts as the one figure chokepoint.
  • console/bridge — the growth port, the fixture bridge, the workflows scenario,
    and workflow-projection.ts's read shapes (which leave through the bridge barrel
    and are deleted with that module when packages/contracts registers the plane).
  • console/frame — the surface registry the family's seat registers into.
  • console/workspace/seatsPANE_KINDS, the pane registry, and OwnerSlotContract.

Wires this family does not have

Surface Wire it needs Where it is owed
Save, import, promote, fork a definition workflow.definitionCreate Not on the port; growth-row append, Spec-017 owns the method
Open a definition; open a pinned version body workflow.definitionRead, workflow.versionRead Same
Cancel / resume a run; resolve a gate; submit a human form; start a run workflow.runCancel, workflow.runResume, workflow.gateResolve, workflow.humanFormSubmit, workflow.runStart On the port under workflow-run-control; no scenario settles a workflow mutation, so each renders a typed refusal
Any workflow timeline beat 24 unregistered workflow.* event types workflow-event-registration
A phase's readable name Lives in the definition body workflow.versionRead serves Not on the port; surfaces show the phase id as a wire value
A run enumeration No registered list operation Spec-017 owes it; no growth row exists
Paging the definition list nextCursor is omitted from the scripted reply Scenario truth, not synthesized

Budgets

The budget set is the spec's eight rows plus the rows sibling families and the launch
harness have registered, and this family mints none: its surfaces are
chrome over the substrate's primitives, and the one new dependency is a lazy chunk
the renderer-initial-bundle subject excludes by name.

Measured on this branch: the initial graph is two assets at 186,584 B gzip,
41.5 % of the 450 kB budget, verdict WITHIN BUDGET (brotli 160,661 B, reported and
never gated). The graph library and its stylesheet land in PhaseGraphCanvas-*,
61,371 B gzip that the entry document's import graph does not reach — which is the
claim the manifest, not the comment, settles.

Residuals, re-derived on this head

The residuals the first version of this body listed were re-checked against the current tree rather than carried:

  • bridge/fixture-growth-port.ts over the line ceiling — closed. The port is 284 lines and its test 221; the attention derivation and the scripted-reply adapter each have their own module.
  • The rail destination could not enumerate anything — closed. WorkflowsDestination.tsx resolves the session it reads from (the one a person chose there, else the one this window last opened, recorded by every route transition that names a session), so the definition enumeration is asked with the required session id.
  • The run controls were locally refused — closed by the run-controls fold below: both controls dispatch through bridge.growth and render the port's own refusal; the hand-composed one is deleted.
  • No endurance locator needed re-pointing — still true, and still checked rather than assumed: the endurance workload opens no workflows surface and every Nothing this family renders is surface or inline placement.
  • The Electron-backed tiers time out on a loaded machine — still a property of those tiers' wall-clock waits in substrate files this family does not touch; it reads as flake on a busy runner and is not caused by this branch.

What remains open is the growth slate: the re-pin picker ships absent because no registered read maps a workflowVersionId to its version chain (workflow.runRead answers one id; workflow.versionRead addresses by definition and version number, which the pane does not hold). Registering workflowVersionChainRead on the slate — a registry row, no Status flip — lands with the bridge-placement commit that follows the console bridge split, and the pane's versionChain parameter is already threaded end-to-end for it.

Screenshot references

All six of this family's references were regenerated on the macos-15 runner by console-screenshot-baselines.yml (mode regenerate, run 33988482268) against this branch's head and committed in 5d75e6deworkflow-builder-definition-{light,dark}, workflow-definitions-browser-{light,dark}, workflow-parked-run-{light,dark} — so the committed set is the one ci.yml's console-screenshot-macos job compares against.

After the run-controls fold, the two workflow-parked-run-{light,dark} references were regenerated again on the same runner (run 34004476246) and committed in 677b9d9d. The delta is exactly the fold: the two footer cells no longer show a hand-composed wire-unregistered refusal reading "the operation is not on the bridge yet"; they offer Cancel this run with its optional-reason disclosure and Resume this run, each under its one-line explanation, and the panel is 5 px taller for the disclosure row. The parked-phase cards above them, the dependency notice, and the header are pixel-identical.

That second regeneration also encoded a regression the review caught and this head fixes: the phase graph's canvas was an empty 20 rem box in both parked-run references. The pair was regenerated a third time on the same runner (run 34008266823) and committed in 6800ff10; the canvas now paints the four fitted phase nodes, their connectors and the attribution plate exactly as the first regeneration did, with everything below the graph unchanged from 677b9d9d. Verified by reading the new PNG against the pre-regression reference, not against its immediate parent.

Since the earlier review head

Two lanes landed between the head the 2026-09-04 review threads were written against and this one; each thread was re-verified against the current tree by reading the code rather than the diff, and every reply names where.

Run controls through the growth port (c01681a9). The workflow run pane mounted its cancel and resume controls as hand-composed refusals saying the operation was "not on the bridge yet", while bridge/growth-operations/workflows.ts has carried workflowRunCancel and workflowRunResume all along — so an operator was told an act was unreachable that nothing had checked. Both controls are now offered unconditionally, a press dispatches through bridge.growth, and the refusal a person reads is the port's own — its origin, its code, its structured operation and slate row — never one this mount site composed about a wire it had never consulted. run-control-dispatch.ts owns the call, the single flight and the settlement: one act per (action, run) through the substrate's subject-scoped holder and generation latch, so a second press while one is outstanding is refused visibly rather than queued or dropped, and an answer arriving after the pane was retargeted installs nowhere. A served act advances a read round the run snapshot joins into its subject key, so the run is read once more per act a person performed and by no timer — packages/contracts registers no workflow.* event type, so there is no stream to subscribe to. unregisteredRunControl and its wire-unregistered member are deleted; every other hard-coded refusal in console/workflows/** was enumerated and either relays a served read's refusal, guards a pane address, or — in the builder's case — makes a claim that is still true, since no workflow.definitionCreate operation exists to reach.

Round-2 delta review fold (4a5ae3b1). Four correctness defects are fixed as normative behaviour: the re-pin target is resolved against the version chain on screen and not merely scoped to the run, so a chain that moves cannot leave a blank picker disagreeing with a named target the submit would still send; the pane host's chosen scope and open pane address are held against the port that served them, so a bridge swap re-mints both instead of asserting a session's emptiness about a daemon that was never asked; a refused graph chunk can be asked for again, reaching the retry the loader was already built for through a banner action drawn from the initial document; and the destination's row openers are held still so RunListItem's memo holds. Two smaller shapes go with them — the builder pane's chrome actions are phrasing content, and the fixture's run-enumeration key is spent from its constant rather than restated. Each fix ships with a test that drives the real control and was proven by perturbation; one control that passed on the defective shape was replaced rather than kept. The structure half repairs eleven claims the family made about itself that the pane move had made false — stylesheet edges, the frame/ deep-import census, the cruise budget's measurement, and the announcement adapter's unverifiable promise — widens the pane board's seat gate so a filled line must name the kinds it claims, and deletes a written-never-read intermediate from WorkflowSlotMount while keeping the door line it was the only reader of.

Substrate-4 rebind (72351f97, merged through #437). The screenshot tier's per-file host reading is baseline-host.ts, and the guard it wraps is a class rather than three module bindings, so this family's own baseline-guard.ts and the browser-tier control that drove its latch are deleted rather than kept beside it — the substrate's control makes every claim that one made and four more, on a required CI job. The layering rule set, its twelve planted trees and its sixteen cases are the substrate's; this family's copies were a strict subset and are gone, and the family's own .dependency-cruiser.mjs block was byte-identical to what the substrate now owns across its two files, so the rules file is taken wholesale and the vocabulary file needs no workflows entry — this family lands no root registrar. What survives the substrate's version of seats/index.ts is this family's claim retirements: nine door specifiers it imports in production carry no @consumedBy tag, and the two knip already counts referenced keep the line-comment form a tag would fail the run on. Every new gate is green — the console-wide directory ceiling, the widened stacked-doc walk, the third catch-stringification shape, the glyph-size home, and all three new layering rules.

Round-3 delta review fold (792b9f30). The regenerated parked-run references encoded a real regression: the phase graph's canvas was an empty 20 rem box. ConsolePaneChrome's .meridian-pane declares no block size, so the column above the canvas resolved every flex: 1 against an indefinite height and the canvas carried only a floor — an indefinite containing block, against which @xyflow/react's inline height: 100% root resolves to zero and clips every node. The canvas now states block-size beside that floor and keeps flex: 1 1 auto, so a bounded seat still grows it, and the browser tier gains the two readings a collapsed root cannot satisfy while isPhaseGraphSettled requires a painted phase rather than a fitted transform alone. The layering harness's cruise, tree and rule counts are count-free or derived; the single-flight case presses twice from one captured control inside one act, which a rendered dispatching flag cannot survive; and thirty-four door tags stop naming T-023p-1C-6 for symbols this family never imports, verified with the TypeScript parser. WorkflowsBrowser.test-support.tsx becomes the family's one home for settle, the probe identities and the page cursor, and run-directory.test.tsx drives every case through the store's own commit-recording probe instead of a local render-call copy. Eleven false header and comment claims are corrected to what the tree actually does.

Round-4 delta review fold (lane/wf-fix-g, 9b00d20ec..e9a93a154). Eighteen findings folded across four commits. The node's session directory settled a rejected read through the growth port's own builder, which stamps call-rejected, while the three sibling reads one family up settle through bridge/readings/read-settlement.ts and keep the daemon's dotted code verbatim — two refusal vocabularies for one seam, and a person met whichever surface they were on; useSessionDirectory now settles through the reading layer like every other growth read, GrowthUnavailable becomes a two-arm union whose rejected arm carries the normalized refusal on a required cause so no caller can mint one that discards a daemon code, and three surfaces that folded every refused directory into not-checked under a sentence asserting the console had not asked (a closed channel read as an idle console that chose not to look) now split on isUnbuiltWireRefusal, minted beside the code it reads: only the unregistered-wire arm keeps not-checked, and a failed read renders the daemon's own code and sentence — fix-first against the pre-change hook (expected 'call-rejected' to be 'session.list_unavailable'). Nine more findings folded into what each is about: the destination/ sheet entered through workflows.css, the shape both headers call forbidden, and now enters through its owner's door with no pixel moved (the one shared selector is overridden at (0,2,0) against (0,1,0), so specificity decides and cascade order never did) — and the gate that could not have caught it now can, isOwningBarrel having treated any index.ts as an owner, so stylesheet-edge-graph.ts computes ownership and its suite drives the offending shape, its repair, and the doorless sub-directory the rule must keep admitting; a fourteen-line census in workflows/index.ts of an exemption the substrate had already removed becomes one ordinary door import and three files' duplicate same-specifier imports merge; WireErrorEnvelope's one-console-home rule had eight violators still reaching src/shared/ and all nine take core/index.js now, the door line landing on the condition the header itself named (two production readers); the served-set sweep cited a file absent from the tree and a suite absent from its own foot, and is two claims now — everything served answers under the scenario that scripts them all, nothing unserved answers under any scenario the fixture plays, read from scenarios/index.ts so a family's scenario is swept the day it lands — plus the counter-arm those cites promised; WorkflowsBrowser.test-support.tsx stayed at zone level when its component moved into browser/ and splits on its own seam (the mount with the component, the probe identities / row factory / settled-page port / act boundary into workflows-probe.test-support.ts, twenty-six importers rebound); the scope picker's refusal splits the way its siblings' now do; readUnknownMember gets the suite its narrowing shipped without, driving the array arm (length and "0") where the two readings differ; four prose cites follow pane-addressing.ts into pane/; fixture/index.ts records the deep edge that keeps four cycles closed. The family-independent pane seat suites are adopted, registerWorkflowPanes taking the task id its surface seat already carried. And the run pane's re-pin picker, which shipped absent because a run carries one opaque workflowVersionId and no registered read takes that id anywhere, gets the read that closes it: workflowVersionChainRead joins the growth ledger on a slate row of its own (the mirror image of the run enumeration's row, naming no wire method because inventing one would be a string traceable to nothing), keyed by the version and nothing else, its reply carrying each version's id and ordinal with the current pin deliberately absent since the caller asked by that id and can compare. createRefusingGrowthPort stays homed in its declaring module for its ~21 cross-family test readers, with the doc naming the three rejected alternatives (the family door, which publishes only what an outside production reader takes; the sub-module door, reachable only intra-family; a test/console/ re-export, the shim AGENTS.md forbids).

The develop merges (lane/wf-fix-g: substrate-6 8b0e62119 at a5a743f07, then browser-terminal 947d9e33c + substrate-7 9445133e8). The substrate-6 merge adopted develop's seat contracts wholesale; this family's ...FIXTURE_SERVED_WORKFLOW_OPERATION_IDS moves into develop's bridge/fixture/fixture-served-operations.ts, its bridge/readings/read-settlement.* pair is the half substrate-6 landed (so the merge sees identical content), answerFromScriptedReply is re-generified over TOperationId with tsc enforcing the read-settlement.test.ts call-site update, and the GrowthUnavailable / settleGrowthRead door lines return to bridge/index.ts for their readers here (run-control-dispatch, definition-directory). The seat-grammar widening (41e6487fb, substrate-7 #440) cherry-picked clean as a8810204fpanes/index.ts auto-merged, its eslint-disable block for the once-unused registry parameter gone now that the parameter is read, the registerWorkflowPanes import and registerWorkflowPanes(registry); // T-023p-1C-6 workflow-run workflow-builder surviving on top, and registerWorkflowSurfaces(surfaces); // T-023p-1C-6 workflows on the family board. A one-identifier form of the same widening drafted on this lane was withdrawn unshipped: it would have refused the multi-board registerSeatFamily(surfaces, panes, projectors) case the repos seat needs, and lacked the two negative controls (an empty list, a call expression) that keep the argument list a grammar over identifier names rather than arbitrary text. console-unit moved 2437 → 2441 tests (the family seat now counting plus the three planted controls); eleven tiers, 334 files / 3351 tests. No workflows/ production file changed across the fold, and the only non-test .tsx touched was frame/ConsoleRoot.tsx (two more boards passed to registerConsoleFamilies plus header prose — no JSX, no styles). The browser-terminal + substrate-7 merge (4914f65b8) was a criss-cross with two merge bases (8b0e6211 and 68ee0388), which is why some files conflicted while showing no diff against either base alone and why every resolution class was derived by comparing the merged blob against each side's blob rather than by reading hunks: 24 conflicts, 22 content and two add/add — develop's file for AGENTS.md, bridge/console-bridge.ts, frame/RouteSurface.tsx, budgets.json, electron-harness.ts, steady-state.test.ts, and tsconfig.console-electron-test.json; ordered unions for package.json, bridge/index.ts, the bridge/scenarios, families.ts, and panes/index.ts seat boards, core/index.ts (both families' constants alphabetical, 1C-6 retired from three @consumedBy tags), primitives/index.ts (nine hunks, then a uniform retirement of fifteen 1C-6 and seven 1C-7 tags), store/index.ts, tsconfig.test.json, and the Plan-023 manifest rows; this family's superset for frame/SessionsSurface.tsx (WireChoiceList through primitives/index.js instead of the retired local file), persistence/index.ts (measureUtf8ByteLength), primitives/wire-figures.ts (the dated-instant formatter), tsconfig.console-browser-test.json, and the add/add axe-run.ts; and for the add/add stylesheet-edges.test.ts, where both branches had authored a whole gate, a measured union — each instrument is red on the other family's tree and all four claims hold over the merged one — composed on develop's superseding TypeScript-parser instrument carrying this family's two model-backed claims and three negative controls; pnpm-lock.yaml never hand-merged (develop's, pnpm install --no-frozen-lockfile, --frozen-lockfile clean; the merged blob equals neither side because regeneration is the rule's expected outcome). 4c4da860d answered three things: cap-constant-home named PHASE_GRAPH_MAX_ZOOM and WORKFLOW_CANCEL_REASON_BYTE_CAP in workflows/constants.ts, so all three constants moved to core/constants.tsPHASE_GRAPH_MIN_ZOOM with them, since it is half of one range decision — exported through the core door, five readers re-pointed, the family's constants module and suite retired, and the header sentence that authorised a per-family module corrected, the same answer the ledger lane gave; four whole-tree parse gates (catch-stringification-chokepoint, no-browser-namespace, resize-observer-chokepoint, stylesheet-edges) crossed vitest's 5 s default once the tree grew by two families while asserting nothing new — verified solo (still red cold) and at 120 s (31/31 green, warm 1.8–2.6 s) and answered with the tree's own remedy, an explicit CONSOLE_PARSE_ALLOWANCE_MS = 30_000 on the source-walk-chokepoint precedent with each measurement recorded, resize-observer-chokepoint never having had a violation against this family; and a vacuity hole the stylesheet union itself opened — two claims moved onto CONSOLE_STYLESHEET_TREE while the floor claim still guarded the Vite glob, so an empty model list would have left both silently green — closed with a cross-check between the two sheet lists, proven by perturbation (.slice(1) fails exactly one test). Gates on that tip were green across all eleven tiers; 80 .tsx / .css under console/ differed from the pre-merge tip with 77 byte-identical to develop, the three others being frame/RouteSurface.test.tsx (develop's suite had reserved the rail's middle destination for this family by task id, so the reserved-slot claim is re-scoped onto a slot deliberately left unclaimed) and two of this family's own files re-pointed at the core door. The collaboration re-merge (a45fb97c7, 14 conflicts, merge-base 9445133e8) was classed file by file by comparing the merged blob against both sides: fixture-growth-port.test.ts is develop's suite with this family's one refusal test re-inserted (a naive both-sides fold had opened the same describe twice); fixture-served-operations.ts takes develop's eight new ids into both halves, the initializer and the isolatedDeclarations tuple annotation; fixture-session-directory.ts, frame/frame.css, and primitives/wire-figures.ts are develop's (its typed state read supersedes the untyped one); frame/SessionsSurface.tsx's retirement stands, this family having no production reader; growth-operations/index.test.ts, bridge/index.ts, core/index.ts (WHEN_CLAUSE_OVERLAP_MAX_CONTEXT_KEYS beside WORKFLOW_CANCEL_REASON_BYTE_CAP), core/constants.ts (develop's fuller licensing header over this family's appended bounds block — 462 lines, the over-400 split being 1C-8's), and tsconfig.test.json are unions; primitives/index.ts and seats/owner-slot.ts are unions of removals; and pnpm-lock.yaml was never hand-merged (develop's copy, pnpm install --no-frozen-lockfile, --frozen-lockfile exits 0). What the collaboration delta forced is 3b5887ff4, five claims the merge left false, each named by a gate: DraftStore's maximumDraftCount became required, so the family's test surface supplies it through the core door in the browser-terminal surface's exact form; OwnerSlotContract came back tagged in seats/index.ts while this family imports it in production, which knip under --treat-tag-hints-as-errors refuses, so the claim is struck with the reason beside the markers that legitimately stay; readUnknownMember lost its last importer when develop's typed session directory replaced the reader built on it, so the door publishes the string read alone and the container read stays private; the fixture's script-only subset was stated too narrowly (develop's "writes have no honest empty answer"), and the real criterion — an answer about a named subject has no empty form, because an empty one asserts the subject exists — brings the three subject-addressed workflow reads in beside the four writes, with a read-side control landing beside develop's write control and the subtraction perturbation-proved (under a scenario scripting no workflow exactly one test fails); and the e2e reserved-slot probe develop had pointed at #/workflows with a note that this family's landing would re-point it again did exactly that — of the seven declared surface slots only timeline is still unclaimed, so the probe now names it, addressed with the scenario's own session because a bare auxiliary route answers with the context picker rather than an absence (a 10,676 ms locator timeout became a 694 ms pass). Because the collaboration delta moved tokens/palette.ts and frame/frame.css, which this family's pinned references render against, the screenshot tier was owed rather than optional and ran clean. Against the pre-merge tip, 226 .tsx / .css under console/ differ and 222 are byte-identical to develop; SessionsSurface.tsx and its test are absent from both trees, builder/slots/slots.test.tsx is this family's own, and frame/ConsoleFrame.tsx differs by exactly this family's five paneRegistry composition-prop lines, whose production reader is workflows/OpenPaneBody.tsx.

Capture determinism (b9d710aca, 471e988c2 — cherry-picked from #427's e0f519377 / fa34d584b). The screenshot tier on the macos-15 runner had been resolving the console's monospace stack two different ways across runs of the same tree: nothing self-hosts IBM Plex, so the DOM face was whichever remaining --meridian-font-mono entry the host had activated (a 0.600-advance face with an unslashed zero in one mode, Menlo at 0.602051 em with a slashed zero in the other), and the terminal grid was drawing in @xterm/xterm's own courier-new, courier, monospace default because xterm-adapter.ts never passed the token it declared on the host — 17 px rows in one mode, 18 px in the other, a 24 px taller pane. Both modes had reached the committed corpus, so every regeneration only moved which references were wrong. The first commit pins --meridian-font-mono to Menlo, monospace per test as an inline root property through the screenshot project's setupFiles (test/console/screenshot/capture-faces.ts + .setup.ts, four-case suite, a negative control that fails 3 of 4 without the wiring), chosen because both observed modes reach Menlo so the arms converge; the second adds terminal/emulator/xterm-typeface.ts, which tells the emulator the host's computed family on attach (five-case suite). The pair was verified on the runner itself before it reached this branch: a regenerate run against the repos head carrying it (run 34043018643) wrote no PNG change. On this head after the cherry-pick: typecheck 0, lint 0, structure 0, xterm-typeface.test.ts 5/5, and the console-architecture tier green. The regeneration on 3b5887ff4 (run 34040980116, committed as b47d71aec) had rewritten six references — the four browser-terminal ones and this family's workflow-parked-run-{light,dark} — in whichever mode that run sampled; after the cherry-pick the regeneration on 471e988c2 (run 34043139872, committed as 5294b5a1b) rewrote exactly two, workflow-definitions-browser-{light,dark}, this family's one surface with monospace text, and left the four browser-terminal references untouched — they are byte-identical to the repos head's 29c06285f copies (git diff --stat across the heads is empty), which is the convergence the pin promises. The compare job on 5294b5a1b is this family's runner-side verification.

The repos re-merge (56b11d23b: develop e17c9b6d3 — the #441 lefthook worktree lock on top of the repos family squash 680260e8a). 302 files, 15 conflicts, every one hand-merged from both sides and none byte-identical to either (recomputed with git merge-tree --write-tree --name-only). Seven ordered unions: families.ts (develop's registerRepos(sidebarSections, inlineCardSeats) beside registerWorkflowSurfaces(surfaces) in seat order), panes/index.ts, the scenario registry (REPOS_SCENARIO + WORKFLOWS_SCENARIO), package.json (@xyflow/react@12.11.6 beside develop's diff@9.0.0), core/constants.ts (both sides' constant groups with both header paragraphs — 651 lines, the split is 1C-8's), constants.test.ts (COUNTING_BOUNDS rows unioned, the phase-graph zoom-range describe appended after develop's four), and fixture-growth-port.test.ts (both declarations and all three cases, develop's branch-context refusal case included). Three doors on the tag-set rule — core/index.ts and primitives/index.ts @consumedBy tags resolved as the intersection of the two sides' surviving lists, a tag deleted outright wherever either side deleted it because that side became a real importer (InstantReading for workflows, formatDuration for repos) — and bridge/index.ts, where both sides had re-added export type { GrowthUnavailable } at different positions, which would have been a duplicate re-export: develop's placement beside GROWTH_PORT_REFUSAL_ORIGIN kept, the door's paragraph rewritten to the merged truth (both symbols travel — repos/growth-call.ts for a rejected call, workflows/pane/run/run-control-dispatch.ts:47,85 naming the refusal arm — verified by grep). Two hand-merges applying each side's rule to the other's code: fixture-served-operations.ts takes develop's corrected gitflow sentence behind the family's served-operation block, and fixture-growth-port.ts takes develop's semantics on this branch's seam — fixture-scripted-answer.ts's fallback is now () => GrowthOutcome<…> keyed on the operation id, so develop's () => undefined plus a scripted.value === undefined re-read is unrepresentable and is said directly as () => growthUnavailable("gitflowBranchContextRead"), the post-check disappearing with its reason. stylesheet-edges.test.ts keeps the family's edge-count case under develop's broadened isDoorModule title; CALL_DOOR_CONSUMER_COUNT re-derived lane 5 / develop 6 / merged 6 (workflows adds no call-door consumer, repos adds one). Plan-023 is an ordered union — develop's T-023p-1C-5 row verbatim beside this family's T-023p-1C-6 row (Files: console/workflows/, confirmed against the tree) and both growth-slate rows. pnpm-lock.yaml never hand-merged: develop's copy, then pnpm install --no-frozen-lockfile, +184 lines that are exactly the @xyflow/react importer entry and its d3-* subtree. No develop-deleted line was kept and no path took this family's side verbatim. No fix commit was needed: typecheck 0, lint 0, structure 0 (969 modules / 3,326 dependencies), test 0 across eleven tiers — renderer 12 / 135, main-unit 14 / 207, console-unit 543 / 5,447, console-architecture 46 / 486, console-assets 1 / 12, console-browser 5 / 22, console-accessibility 7 / 48, console-bundle 9 / 68, smoke 3 / 42, console-e2e 1 / 5, console-endurance 8 / 40 — first time, no re-runs. Rendered surface: 92 .tsx / .css files under console/ differ from 5294b5a1b and all 92 are byte-identical to develop and under console/repos/ — develop's family arriving whole — so not one workflows .tsx or .css changed and no shared rendered surface moved.

Codex. Code-review credits are exhausted account-wide; the merge records one codex-gate attempt against the PR head 56b11d23b: rate_limited (2026-09-06, the gate's NON-ack terminal — no findings were produced, so none are folded).

Gates (on 3b5887ff4; each its own invocation, TURBO_DAEMON=false)

Gate Exit
pnpm typecheck 0
pnpm lint 0
structure (knip + dependency-cruiser: 813 modules, 2,713 dependencies, no violations) 0
test (eleven tiers via turbo) 0
test:console-screenshot (owed: the collaboration delta moved tokens/palette.ts and frame/frame.css) 0 — 25 passed, 25 skipped off the pinned host

Per tier: renderer 135, main-unit 207, console-unit 4,480, console-architecture 483, console-assets 12, console-browser 18, console-accessibility 32, console-bundle 68, smoke 42, console-e2e 5, console-endurance 31. Every clean assertion has a negative control. pnpm install --frozen-lockfile exits 0.

This lane adds or changes 233 files, 204 of them under console/ — 103 production modules (71 added, 31 modified, 1 renamed) across 30 directories, of which the workflows family itself is 60 production modules across 12 directories; the largest file the family authors is 400 lines (pane/run/phase-graph/phase-graph.css), and the one source file over 400 lines this branch carries is the shared core/constants.ts at 462, whose split is T-023p-1C-8's.

Refs: Spec-023, Plan-023

🤖 Generated with Claude Code

Co-Authored-By: Claude Fable 5.1 noreply@anthropic.com

Review notes — jaw-drop verdict (retroactive, 2026-09-06)

Verdict: NO (would not screen-record and post unprompted as merged). Reason: the definitions browser can enumerate and open nothing, the park attention fold is projected but never rendered, and a run in another window's pane never updates live.

Fix pass: the 2026-09-06 console coverage wave (Plan-023 Phase 1C follow-up PRs). Re-check: a second binary verdict is recorded on the wave PR that closes these rows; never an open loop.

@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for security reviews. Please try again later.

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Sep 2, 2026

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review Completed 2026-09-04T21:57:39.719346Z c6fdd87 New commits
🔒 Security Review Completed 2026-09-06T16:23:02.682954Z 56b11d2 New commits
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@Sawmonabo

Copy link
Copy Markdown
Owner Author

@codex review

@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for security reviews. Please try again later.

@chatgpt-codex-connector chatgpt-codex-connector Bot 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 5497b72d94

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread apps/desktop/test/console/screenshot/workflows.test.tsx
Comment thread apps/desktop/src/renderer/src/console/workflows/index.ts Outdated
Comment thread apps/desktop/src/renderer/src/console/bridge/scripted-reply-adapter.ts Outdated
Comment thread apps/desktop/src/renderer/src/console/workflows/definition-directory.ts Outdated
Comment thread apps/desktop/src/renderer/src/console/workflows/RunList.tsx Outdated
Comment thread apps/desktop/src/renderer/src/console/panes/workflow-run/slots/HumanFormSlot.tsx Outdated
Comment thread apps/desktop/src/renderer/src/console/workflows/workflows.css Outdated
Comment thread apps/desktop/src/renderer/src/console/workflows/WorkflowsBrowser.tsx Outdated
Comment thread apps/desktop/src/renderer/src/console/bridge/scenarios/workflows.ts Outdated
Comment thread apps/desktop/src/renderer/src/console/workflows/run-list-projection.ts Outdated
@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for security reviews. Please try again later.

2 similar comments
@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for security reviews. Please try again later.

@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for security reviews. Please try again later.

@chatgpt-codex-connector chatgpt-codex-connector Bot 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 7f47716a51

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread apps/desktop/src/renderer/src/console/workflows/WorkflowsDestination.tsx Outdated
Comment thread apps/desktop/src/renderer/src/console/workflows/ParkBadge.tsx Outdated
Comment thread apps/desktop/src/renderer/src/console/workflows/run-list-projection.ts Outdated
Comment thread apps/desktop/src/renderer/src/console/workflows/WorkflowRuns.tsx Outdated
@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for security reviews. Please try again later.

@Sawmonabo

Copy link
Copy Markdown
Owner Author

@codex review

@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for security reviews. Please try again later.

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Something went wrong. Try again later by commenting “@codex review”.

Unknown error
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@chatgpt-codex-connector chatgpt-codex-connector Bot 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 992b4b4f78

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread apps/desktop/src/renderer/src/console/bridge/fixture-growth-port.ts Outdated
Comment thread apps/desktop/src/renderer/src/console/bridge/scenarios/workflow-fixture-data.ts Outdated
Comment thread apps/desktop/src/renderer/src/console/panes/workflow-run/WorkflowRunPane.tsx Outdated
Comment thread apps/desktop/src/renderer/src/console/workflows/ParkBadge.tsx Outdated
@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for security reviews. Please try again later.

@chatgpt-codex-connector chatgpt-codex-connector Bot 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 66951eb759

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread apps/desktop/src/renderer/src/console/workflows/WorkflowsPaneHost.tsx Outdated
Comment thread apps/desktop/src/renderer/src/console/workflows/DefinitionsBrowser.tsx Outdated
Comment thread apps/desktop/src/renderer/src/console/workflows/WorkflowsDestination.tsx Outdated
Comment thread apps/desktop/src/renderer/src/console/workflows/WorkflowsSurface.tsx Outdated
Comment thread apps/desktop/src/renderer/src/console/panes/workflow-run/WorkflowRunPane.tsx Outdated
Comment thread apps/desktop/src/renderer/src/console/workflows/WorkflowChrome.tsx Outdated
Comment thread apps/desktop/src/renderer/src/console/workflows/RunList.tsx Outdated
@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for security reviews. Please try again later.

@chatgpt-codex-connector chatgpt-codex-connector Bot 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: e8c56bd4f3

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread apps/desktop/src/renderer/src/console/panes/workflow-run/WorkflowRunPane.tsx Outdated
Comment thread apps/desktop/test/console/accessibility/workflows-axe.test.tsx
@Sawmonabo
Sawmonabo changed the base branch from feat/plan-023-phase-1c-seats to develop September 2, 2026 22:36

@chatgpt-codex-connector chatgpt-codex-connector Bot 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 540f0dd721

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread apps/desktop/test/console/architecture/stylesheet-edges.test.ts Outdated
Comment thread apps/desktop/src/renderer/src/console/bridge/scenarios/workflow-fixture-data.ts Outdated

@chatgpt-codex-connector chatgpt-codex-connector Bot 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 6bc9eeb3c8

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread apps/desktop/src/renderer/src/console/workflows/ParkBadge.tsx Outdated
Comment thread apps/desktop/src/renderer/src/console/workflows/run-list-rows.ts Outdated
Comment thread apps/desktop/src/renderer/src/console/workflows/DefinitionsBrowser.tsx Outdated

@chatgpt-codex-connector chatgpt-codex-connector Bot 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: c613298ba2

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread apps/desktop/src/renderer/src/console/bridge/fixture-growth-port.test.ts Outdated
Comment thread apps/desktop/src/renderer/src/console/workflows/DefinitionsBrowser.tsx Outdated
Comment thread apps/desktop/src/renderer/src/console/workflows/definition-directory.ts Outdated
Comment thread apps/desktop/test/console/accessibility/axe-run.ts Outdated
Comment thread apps/desktop/src/renderer/src/console/workflows/WorkflowsBrowser.tsx Outdated

@chatgpt-codex-connector chatgpt-codex-connector Bot 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: fcd05a4994

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread apps/desktop/src/renderer/src/console/workflows/run-directory.ts Outdated
Comment thread apps/desktop/src/renderer/src/console/panes/workflow-run/run-snapshot.ts Outdated
Comment thread apps/desktop/src/renderer/src/console/workflows/WorkflowsBrowser.tsx Outdated
Comment thread apps/desktop/src/renderer/src/console/panes/workflow-run/WorkflowRunPane.tsx Outdated
Comment thread apps/desktop/src/renderer/src/console/bridge/scenarios/workflows.test.ts Outdated
Comment thread apps/desktop/package.json Outdated

@chatgpt-codex-connector chatgpt-codex-connector Bot 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 4bbdac659a

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread apps/desktop/src/renderer/src/console/workflows/run-list-rows.ts Outdated
Comment thread apps/desktop/src/renderer/src/console/workflows/WorkflowsScopePicker.tsx Outdated
Comment thread apps/desktop/src/renderer/src/console/panes/workflow-run/WorkflowRunPane.test.tsx Outdated
Comment thread apps/desktop/src/renderer/src/console/workflows/WorkflowsBrowser.test.tsx Outdated
Comment thread apps/desktop/src/renderer/src/console/bridge/scenarios/workflows.ts Outdated
Comment thread apps/desktop/src/renderer/src/console/workflows/run-list-projection.ts Outdated
Comment thread apps/desktop/src/renderer/src/console/workflows/WorkflowsPaneHost.tsx Outdated

@chatgpt-codex-connector chatgpt-codex-connector Bot 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: c6fdd87bf3

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread apps/desktop/src/renderer/src/console/workflows/WorkflowsBrowser.tsx Outdated
Comment thread apps/desktop/src/renderer/src/console/workflows/definition-directory.ts Outdated
Comment thread apps/desktop/src/renderer/src/console/workflows/run-list-projection.test.ts Outdated
Comment thread apps/desktop/src/renderer/src/console/bridge/scenarios/workflows.ts Outdated
Comment thread apps/desktop/src/renderer/src/console/workflows/DefinitionsBrowser.test.tsx Outdated
Comment thread apps/desktop/test/console/accessibility/workflows-axe.test.tsx
Sawmonabo and others added 5 commits September 4, 2026 18:25
A first page carrying `nextCursor` and no row for a scope establishes nothing about
that scope: the cursor pages the whole resolved union at once. The groups said
"No project definitions" anyway, and kept saying it while the continuation read and
after the daemon refused it.

What a group with no rows may claim is now projected from the continuation the read
already carries, beside the pages themselves, and threaded to the groups untouched.
Three absences, three next moves: a wait while a page is arriving, "read on" while
pages are unread, and a definitive empty only once the daemon says there are no more.

A refused continuation lands on the same unresolved arm as an unfollowed cursor,
deliberately. The daemon's own sentence is rendered once under the groups, beside
the control that retries it, rather than three times over as three empty results.

Refs: Spec-023, Plan-023
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
run-list-rows.test.ts carried a byte-identical private copy of the
builder the run-list-projection split hoisted; one home, both suites.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The scope picker offers the union of the node's session directory and this window's
own open sessions. With the directory refused and one session open, the union still
returned that session, the zero-length refusal branch was skipped, and the picker
presented the one session as though it were the complete node list.

That is the release build's path rather than a corner: the live bridge refuses
`sessionList` by name, so an operator who pressed "choose a different session" was
shown the session already open and nothing saying the rest had not been read.

The choices stay, because withdrawing a session this window genuinely holds helps
nobody, and the daemon's refusal is rendered beside them through the same
partial-read shape the definitions browser uses for a refused continuation. The
console's own sentence about what it managed to offer stands beside the daemon's
code and message rather than paraphrasing either.

Refs: Spec-023, Plan-023
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Rewritten by the console-screenshot-baselines workflow (mode: regenerate,
run 34008266823) against this branch's head, so the committed references are
the ones ci.yml's console-screenshot-macos job compares against.

Refs: Plan-023
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@chatgpt-codex-connector

Copy link
Copy Markdown

Codex usage limits have been reached for code reviews. Please check with the admins of this repo to increase the limits by adding credits.
Repo admins can enable using credits for code reviews in their settings.

Sawmonabo and others added 13 commits September 6, 2026 00:37
# Conflicts:
#	apps/desktop/src/renderer/src/console/bridge/fixture/fixture-growth-port.test-support.ts
#	apps/desktop/src/renderer/src/console/bridge/fixture/fixture-growth-port.test.ts
#	apps/desktop/src/renderer/src/console/bridge/fixture/fixture-growth-port.ts
#	apps/desktop/src/renderer/src/console/bridge/fixture/fixture-scripted-answer.ts
#	apps/desktop/src/renderer/src/console/bridge/fixture/fixture-session-directory.ts
#	apps/desktop/src/renderer/src/console/bridge/growth-port/growth-outcome.ts
#	apps/desktop/src/renderer/src/console/bridge/index.ts
#	apps/desktop/src/renderer/src/console/bridge/scenarios/index.ts
#	apps/desktop/src/renderer/src/console/families.ts
#	apps/desktop/src/renderer/src/console/frame/ConsoleRoot.routing.test.tsx
#	apps/desktop/src/renderer/src/console/frame/ContextPicker.tsx
#	apps/desktop/src/renderer/src/console/frame/SessionsAbsence.tsx
#	apps/desktop/src/renderer/src/console/frame/SessionsSurface.tsx
#	apps/desktop/src/renderer/src/console/primitives/wire-figures.ts
#	apps/desktop/src/renderer/src/console/seats/session-directory.test.tsx
#	apps/desktop/test/console/architecture/console-layering-rules.test.ts
#	apps/desktop/test/console/architecture/console-layering-trees.ts
The console bridge split (substrate-5) landed on develop; this folds the workflows
family onto it and places what the family adds.

Bridge placement, one directory per concern: the two fixture answer builders and
the scripted-answer suite to `fixture/`, the untyped-member reads and the scripted
probe identity to `scenario-runtime/` beside the seam they serve, and the growth
read settlement to a new `readings/` with no door — every reader is a view family,
so an inner barrel there would publish a name no sibling reaches.

The family's own `bridge/session-directory.ts` is deleted for the substrate's
`seats/session-directory.ts`, which is the same hook with a rejection arm; its
three consumers rebind through the seats door. `WireChoiceList` stays hoisted to
`primitives/` and the frame's two readers rebind to it.

Zone grouping: `browser/` and `destination/` each become a module with a door a
sibling takes from. `pane-addressing.ts` moves beside its two readers in `pane/`.
The shared probe scaffolding stays at zone level — fourteen importers across four
sub-modules make it the family's, not the browser's.

The untyped-member read now takes `core`'s `isWireRecord` instead of restating the
container check, and the wire envelope a harness types reaches `core`'s declaring
module rather than `src/shared/`.

Refs: Spec-023, Plan-023
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The node's session directory settled a rejected read through the growth
port's own builder, which stamps `call-rejected` and composes a sentence
around whatever the other side said. The three sibling reads one family up
settle through `bridge/readings/read-settlement.ts`, which keeps the
daemon's dotted code and message verbatim — so one surface showed
`call-rejected` where another, one navigation later, showed
`workflow.session_not_found` for the same class of failure. Two refusal
vocabularies for one seam, and a person met whichever surface they were on.

`useSessionDirectory` now settles through the reading layer like every
other growth read in the console. The port's builder keeps `call-rejected`
as its own classification but stops dropping what it read: `GrowthUnavailable`
becomes a two-arm union whose rejected arm carries the normalized refusal on
a required `cause`, so no caller can mint one that discards a daemon code.
The builder has no production caller today and its own header says so; a
code the vocabulary carries and nothing can construct would be worse, and
its new co-located suite drives what it produces.

Three surfaces then folded EVERY refused directory into `not-checked` under
a sentence asserting the console had not asked the daemon, with the refusal
appended — so a closed channel read as an idle console that chose not to
look. `isUnbuiltWireRefusal` answers which kind of nothing a refusal is,
once, beside the code it reads; only the unregistered-wire arm keeps
`not-checked`, and a failed read renders the daemon's own code and sentence.

Fix-first, against the pre-change hook:
  expected 'call-rejected' to be 'session.list_unavailable'
  expected 'call-rejected' not to be 'call-rejected'
  expected 'call-rejected' to be 'growth-read-call-failed'

`growthUnavailableFromRejection` comes off `bridge/index.ts`: its only
importer is now the suite beside it, which is the class `barrel-census`
fails on a door.

Refs: Spec-023, Plan-023
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Nine review findings against this family, folded into what each one is about
rather than recorded beside it.

STYLESHEET OWNERSHIP. The placement gave `destination/` a door and left its
sheet entering through `workflows.css`, which is the shape both that sheet's
own header and the package rules call forbidden — a sheet in one directory
made the reason another is styled at all — while `destination/index.ts`
asserted the opposite rule about the same file. The sheet now enters through
its owner's door and the family sheet's "three doors below this one" is
count-free, since a count in prose goes stale the next time a door lands.
No pixel moves: the one selector the two sheets share is overridden at
`(0,2,0)` against `(0,1,0)`, so specificity decides it and cascade order
never did.

The gate could not have caught it — `isOwningBarrel` treated any `index.ts`
as an owner and the walk asserted one edge from a barrel, never one edge from
the OWNER. It computes ownership now, and its own suite drives the offending
shape, its repair, and the doorless sub-directory the rule must keep
admitting. Against the pre-fix tree:
  workflows/destination/workflows-destination.css: owned by
  workflows/destination/index.ts, reached from workflows/workflows.css

TWO HEADERS THAT DESCRIBED A DELETED CONFIG. `workflows/index.ts` carried a
fourteen-line census of an exemption the substrate had already removed, and
sent a reader to find a `to.pathNot` entry that is in neither cruiser config.
Both boards live in `seats/` now, so the import is one ordinary door and the
comment says that instead. Three files' duplicate same-specifier imports
merge with it.

THE ENVELOPE SHAPE HAS ONE CONSOLE HOME. One module was moved onto `core`'s
re-export of `WireErrorEnvelope` under a header stating the one-home rule
while eight others kept reaching `src/shared/` — a rule with eight
violations. All nine take `core/index.js` now, and the door line lands on the
condition that header itself named: two production readers. The claim is also
scoped to what it governs — the SHAPE — since the envelope's readers are
still reached from the leaf by two suites, and a door line for those would be
the census failure the same note records.

A SET PROVEN UNDER ONE SCENARIO, JUSTIFIED BY A SUITE THAT DOES NOT EXIST.
The served-set sweep cited a file absent from the tree and a workflow suite
absent from its own foot. It is two claims now: everything served answers
under the scenario that scripts them all, nothing unserved answers under ANY
scenario the fixture plays — read from `scenarios/index.ts`, so a family's
scenario is swept the day it lands — plus the counter-arm those cites
promised, a served workflow read still refusing where no script speaks.

THE HARNESS THE BROWSER LEFT BEHIND. `WorkflowsBrowser.test-support.tsx`
stayed at zone level when its component moved into `browser/`, so a module
named for a component sat where that component was not and reached past the
new door to find it. Its four browser importers were four of twenty-six. It
splits on the seam its own header drew: the mount goes with the component,
and the probe identities, the row factory, the settled-page port and the
`act` boundary become `workflows-probe.test-support.ts`.

Also: the scope picker's refusal splits the way its siblings' now do, and its
verbatim-code claim is pinned rather than asserted; `readUnknownMember` gets
the suite its narrowing shipped without, driving the array arm that is the
only place the two readings differ; four prose cites follow
`pane-addressing.ts` into `pane/`; `fixture/index.ts` records the deep edge
that keeps four cycles closed; and two frame cases stop explaining
themselves by a branch this family already ships on.

Refs: Spec-023, Plan-023
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Six family branches each fill their own seat in `registerConsolePanes` and in
`families.ts`, and both suites only stay one-line diffs across those branches
while every branch carries them byte-identical. Adopted from lane/seat-probe
742e4a7: the board is read as a census over its own grammar rather than
compared against an ordered literal of the exact seat lines, which is
family-specific by construction.

This family's pane registration carried no task id — `registerWorkflowPanes`
with a bare kind list — so the census reported it twice over, once as an
unmarked line inside a seat group and once as a seat 6 that no group filled.
It takes the id its surface seat already carries.

Refs: Spec-023, Plan-023
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The run pane's re-pin picker shipped ABSENT, and the comment beside it said why: a run
carries one opaque `workflowVersionId` and no registered read takes that id anywhere.
`workflow.runRead` answers the pin; `workflow.versionRead` addresses a version by
`(definitionId, versionNumber)`, which a caller holding a pin holds neither half of; the
definition enumeration carries only each definition's latest. So no re-pin target could
be NAMED, and an operator repairing a frozen pin had nothing to choose from.

Register the read that closes it. `workflowVersionChainRead` joins the growth ledger on
a slate row of its own — the mirror image of the run enumeration's row, and like it
naming no wire method, because inventing `workflow.versionChainRead` would be a string
traceable to nothing. Its request is keyed by the version and by nothing else, and its
reply carries each version's id and ordinal: the id is what a re-pin travels with, the
ordinal is what a person reads instead of it, and which entry is the current pin is
absent on purpose, since the caller asked by that id and can compare.

The pane reads it through the family's own settlement path, keyed by the pin the served
snapshot reports. That ordering is why the dispatcher stops taking a chain: its round is
what puts the snapshot the chain is addressed by, so a chain handed to it would have had
to be resolved before the value it resolves from existed. `WorkflowResumeDispatch` splits
out of `WorkflowResumeControl` for that — one interface per producer — and the pane
joins them at the mount, which is the one place both are in hand.

Every arm that is not served offers nothing: unasked, in flight, refused by the port,
refused by a daemon, rejected at the seam. The picker stays absent rather than empty and
the resume travels with no re-pin, because synthesising a chain from the one id in hand
would offer a target nobody read. The refusal is deliberately not rendered — under a
live bridge that would put a banner about an unregistered wire beside a control the
operator can still press.

The workflows fixture answers per requested version out of the chains its definition
table now holds, complete rather than sampled (a definition at version N has N versions),
and refuses a version it states nothing for through the one not-found constructor the
scope module owns.

Controls: the pane renders exactly the chain the fixture answers, in the order answered,
with the current pin marked; the absent arm renders no picker and still offers resume.
Planted negative controls both fail on the guardless code — synthesising on the unserved
arm fails two hook cases, and addressing the read with the run id fails the pane's absent
arm with a picker whose only target is the run.

Refs: Spec-023, Plan-023
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
`ScenarioRejectingReply`'s note put the console's one home for the wire's
`{code, message}` shape and for `normalizeWireRejection` in
`src/shared/wire-errors.ts`. The normalizer is not there — it is declared in
`core/wire-rejection.ts`, which is also the module that re-publishes the
envelope, and this file has read the shape through `core/index.js` since the
rebind that gave that door its production readers.

Stated against the door the module actually reads, with the reason the rule
exists: `src/shared/` sits on no rung of the console's family DAG, so taking
the shape from `core` is what keeps one reading of it above that floor
rather than one per family.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Substrate-6 lands the five cross-cutting commits on develop once. Thirteen
conflicts, resolved to the rules the fold plan sets out:

- seam paths (readings/read-settlement.test.ts, frame/SessionsAbsence.test.tsx,
  seats/session-directory.frames.test.tsx) and the seat-adoption list
  (families.test.ts, panes/panes.test.ts, seats/{index,pane-registry}.ts) take
  develop's; panes/seat-census.test-support.ts is deleted where it moved.
- the doors take develop's plus this family's own lines. GrowthUnavailable,
  settleGrowthRead and the workflow projection block come back because this
  family supplies the readers develop had none for, and the paragraph
  justifying their removal goes with them.
- the served workflow operations re-home into fixture-served-operations.ts,
  spread into the annotated tuple so the ids and the handlers stay one set.
- families.ts hands registerWorkflowSurfaces the surface board by its own name,
  and the two scripted-reply call sites answer with the whole outcome, which is
  what this family's operation-keyed adapter asks for.

Eleven @consumedby markers on seats/index.ts are deleted: this family's modules
are the readers they were holding a place for, and a marker that outlives its
consumer fails the run rather than rotting.

One red is left deliberately. seat-census.test-support.ts pins a filled seat to
the literal `register<X>(registry);` — the pane board's parameter name, which
the family board does not have — so this family's seat line cannot match it.
The widening belongs to the substrate rather than to six family branches, and
lands from its own lane.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The seat census pinned a filled seat to `register<X>(registry);` — an
argument list of exactly one name, and that name spelled `registry`. It
reads the pane board, whose one parameter is called that, and it can
never read `console/families.ts`, which hands out five registries and so
has no parameter by that name. Every family seat therefore parsed as an
unmarked line, the seat block began at the first still-reserved comment
below it, and the census answered `missing-task` for the ordinal that had
just been filled: a board refusing the one edit it exists to accept. All
six family branches hit it at their first seat.

The registration now takes the comma-separated NAMES of the boards it
writes into. Identifiers and not arbitrary text, so the grammar stays a
grammar: an empty list and a call expression are both unmarked lines,
each with a planted case, and a seat needing a value that is not a board
names it above the block and passes that name. The pane board's own
seats are unchanged, and its header says why a single parameter is an
instance of the list rather than the rule.

Refs: Plan-023

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
# Conflicts:
#	apps/desktop/AGENTS.md
#	apps/desktop/package.json
#	apps/desktop/src/renderer/src/console/bridge/console-bridge.ts
#	apps/desktop/src/renderer/src/console/bridge/index.ts
#	apps/desktop/src/renderer/src/console/bridge/scenarios/index.ts
#	apps/desktop/src/renderer/src/console/core/index.ts
#	apps/desktop/src/renderer/src/console/families.ts
#	apps/desktop/src/renderer/src/console/frame/RouteSurface.tsx
#	apps/desktop/src/renderer/src/console/frame/SessionsSurface.tsx
#	apps/desktop/src/renderer/src/console/panes/index.ts
#	apps/desktop/src/renderer/src/console/persistence/index.ts
#	apps/desktop/src/renderer/src/console/primitives/index.ts
#	apps/desktop/src/renderer/src/console/primitives/wire-figures.ts
#	apps/desktop/src/renderer/src/console/store/index.ts
#	apps/desktop/test/console/accessibility/axe-run.ts
#	apps/desktop/test/console/architecture/stylesheet-edges.test.ts
#	apps/desktop/test/console/budget/budgets.json
#	apps/desktop/test/console/electron-harness.ts
#	apps/desktop/test/console/endurance/steady-state.test.ts
#	apps/desktop/tsconfig.console-browser-test.json
#	apps/desktop/tsconfig.console-electron-test.json
#	apps/desktop/tsconfig.test.json
#	docs/plans/023-desktop-shell-and-renderer.md
#	pnpm-lock.yaml
Two gates develop landed while this family was in flight fail over the merged
tree, and both are real rather than cosmetic.

`cap-constant-home.test.ts` says a ceiling lives in one module and no view
family declares one of its own. This family declared two, and the header it
was written against — `core/constants.ts` saying a family adds its own module
beside its subtree — is the sentence that gate exists to retire. So the cancel
reason's byte cap and the phase graph's zoom range move to the home and reach
this family through the core door, on the precedent the terminal family's caps
already set. The zoom FLOOR travels with its ceiling although its name carries
no bound segment: the two are one decision about what the graph is for, and
split across two homes one of them moves alone. The family's constants module
and its suite are retired, its relations folded into the home's own.

Four whole-tree parse gates then began timing out on vitest's 5 s default —
`catch-stringification-chokepoint`, `no-browser-namespace`,
`resize-observer-chokepoint`, and the stylesheet gate. None asserts anything
new; the tree grew by two view families and the passes crossed the default.
Measured warm they are 1.8-2.6 s and on a cold cache or under the aggregate's
five-project concurrency they cross 5 s, which is the load class
`source-walk-chokepoint.test.ts` documents and answers with an explicit
budget. Each states one now, at that file's figure and for its stated reason.

The stylesheet gate also gains the control the merge owed it: its reachability
claims walk the shared walk's tree while its floor claim guards Vite's module
graph, so an empty model sheet list would have left both silently green. The
two lists are now compared. Proven by perturbation - shortening the model list
by one fails that test and only that test.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
# Conflicts:
#	apps/desktop/src/renderer/src/console/bridge/fixture/fixture-growth-port.test.ts
#	apps/desktop/src/renderer/src/console/bridge/fixture/fixture-served-operations.ts
#	apps/desktop/src/renderer/src/console/bridge/fixture/fixture-session-directory.ts
#	apps/desktop/src/renderer/src/console/bridge/growth-operations/index.test.ts
#	apps/desktop/src/renderer/src/console/bridge/index.ts
#	apps/desktop/src/renderer/src/console/core/constants.ts
#	apps/desktop/src/renderer/src/console/core/index.ts
#	apps/desktop/src/renderer/src/console/frame/SessionsSurface.tsx
#	apps/desktop/src/renderer/src/console/frame/frame.css
#	apps/desktop/src/renderer/src/console/primitives/index.ts
#	apps/desktop/src/renderer/src/console/primitives/wire-figures.ts
#	apps/desktop/src/renderer/src/console/seats/index.ts
#	apps/desktop/src/renderer/src/console/seats/owner-slot.ts
#	apps/desktop/tsconfig.test.json
Four of them, each named by a gate rather than found by reading.

`seats/index.ts` — `OwnerSlotContract` came back from develop carrying a task
marker while this family already imports the type. A marker that outlives its
consumer fails the dead-code gate under `--treat-tag-hints-as-errors`, so it
leaves in the diff that imports the symbol, which is the rule the package
standard already states.

`scenario-runtime` — `readUnknownMember` lost its last importer when develop's
typed session directory replaced the reader that took it. The door publishes
the string read alone now, the container read it is built on stays private,
and the header counts two importers instead of three.

`fixture-served-operations.ts` — the script-only subset widens from writes to
the criterion it was always naming: an answer ABOUT a named subject has no
honest empty form, because an empty one asserts the subject exists. The three
subject-addressed workflow reads join the four writes there. A read-side
control lands beside the write control so the subtraction is not the same act
as deleting the coverage; driven under a scenario that scripts no workflow it
fails, once.

`e2e/frame-boot` — the reserved-slot probe pointed at `#/workflows`, which
this family now owns, so it asserted the reserved arm against a filled slot.
Re-pointed to `timeline`, the one declared surface slot still unclaimed, and
addressed with the scenario's own session because a bare auxiliary route
answers with the context picker rather than an absence.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.

Rewritten by the console-screenshot-baselines workflow (mode: regenerate,
run 34040980116) against this branch's head, so the committed references are
the ones ci.yml's console-screenshot-macos job compares against.

Refs: Plan-023
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.

Sawmonabo and others added 2 commits September 6, 2026 11:39
The screenshot tier pinned its runner and every condition Playwright can be told,
and still could not be green: on `macos-15` the console's monospace stack resolves
two ways across runs of the SAME tree. Nothing self-hosts IBM Plex, so the face is
decided by whichever remaining entry the host has activated, and that answer moved
between one run and the next — a 0.600-advance face with an unslashed zero in some
runs, Menlo in others.

A 0.002 em difference is not cosmetic. It moves the browser capture card's
`Open preview` control one pixel and grows the terminal pane by 24 px, its 24-row
grid one pixel taller per row. Both modes reached the committed set: the browser
and terminal references were minted under the first and the repos references under
the second, so each regeneration only moved which half of the corpus was wrong.

So the tier states the face instead of inheriting it. `capture-faces.ts` pins the
monospace custom property per test through a project setup file, which a family
added later inherits without opting in. Menlo is the value because it is the face
macOS registers system-wide and the one both observed modes reach — by name where
name lookups work, through the generic where they do not — so the two arms converge
rather than alternate. The sans stack is deliberately left alone: it has rendered
identically in every run observed, and pinning it would re-mint every reference in
the repository to close a hazard with no instance.

Measured on a macOS 26.6.1 host, which had been rendering the first mode: the four
references this drift had split now compare byte-clean against the committed set,
and the whole tier's residue is the six documented pixels on a single sans keycap
glyph. `screenshot-pins.ts` no longer credits `ui-monospace` for a residue it no
longer contributes to.

Refs: Plan-023

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
(cherry picked from commit e0f5193)
`emulator.css` sets `font-family: var(--meridian-font-mono)` on the terminal host,
and the grid never read it. `@xterm/xterm` measures its cell from its OWN
`fontFamily` option and never from the element it was opened onto, and this family
passed none — so the declaration reached the accessible row list and nothing else,
while the grid drew in the library's default `courier-new, courier, monospace`.
Two faces for one role, and the row height that sizes the whole pane came from the
one nobody chose.

That is also why pinning the capture typeface did not settle the terminal
reference: the pin moves a custom property the emulator was not reading. Attach now
reads the host's computed family and tells the emulator, so the stylesheet's own
declaration is what decides — one source of truth, a document that overrides the
property is followed, and no copy of a token value lives in the emulator.

A host that declares nothing leaves the library's default alone rather than
clearing it, because a grid with no face draws nothing; and a face that has not
moved is not rewritten, because the write re-measures the cell and repaints every
row while attach runs on every remount.

Measured with the capture pin in place: the two degraded-lease references, which a
macOS 26.6.1 host rendered 24 px short of the committed images, now compare
byte-clean against them.

Refs: Plan-023

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
(cherry picked from commit fa34d58)
@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.

Rewritten by the console-screenshot-baselines workflow (mode: regenerate,
run 34043139872) against this branch's head, so the committed references are
the ones ci.yml's console-screenshot-macos job compares against.

Refs: Plan-023
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.

@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.

@Sawmonabo
Sawmonabo merged commit 86bc6d0 into develop Sep 6, 2026
25 of 28 checks passed
@Sawmonabo
Sawmonabo deleted the feat/plan-023-phase-1c-workflows branch September 6, 2026 16:35
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.

1 participant