Repository navigation
cockpit v3: switch and node routes, header and status line, level strip, children (W1-W4) - #206
Merged
Merged
Conversation
LittleChimera
force-pushed
the
factory/v3-web-shell
branch
from
November 3, 2026 19:42
9ebaaa3 to
1021aa1
Compare
… children Cockpit v3 (the work levels) behind the cockpit choice: `?cockpit=v3` puts the node pages in the new cockpit's shell, v1 and v2 stay as they are, and v2 stays the default for people who chose the new cockpit. The server stores only v1 or v2, so this browser keeps a v3 choice. - Routes `/factory/:id/n/:ref` and `/n/:ref/:level`; Home is the product's node page under v3. A v3 link opened on v1 or v2 lands on the closest page there (the project, the PR, else Home). - `useNode` / `useLevel` (TanStack Query, 5 s fresh, read ahead on hover and focus) against the api.md shapes, through a `NodeSource` seam: the server's node API when preflight says `node_api`, else a sample factory (the shape of a real factory at 13:40, neutral names) computed by a port of the prototype's engine, labelled as sample on the page. - NodeHeader (five lines, then verbs), StatusLine full and compact with "the ask above", WhyCard, LinkChip and ChipList, DecisionButtons (Enter never answers). - LevelStrip and PhoneLevelRow: ancestors (folded implicit ones in italics), levels below with counts, "+n later", "n you" and a state bar, the Results cell; empty levels left out, never a zero cell. - NodeTile, NodeRow, OrderColumns (First / Then / Later), DoneLine, LaterBox, LevelView (grouped under parents, done groups folded, 60-row pages), rows past 12, grouped by state past 60; acceptance and evidence above the children. - Roving tabindex over tile and row links; j/k move focus, Enter or l step in, h steps out, w opens why; KeyboardHelp lists them under v3. - Tests assert no SVG line or path between nodes and no "0" or "none" cell on every sample page, desktop and phone. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Past 60 children, proposals get a "Proposed" group and held work keeps its box; the server's next 60 are read with the page's cursor (`children_page`, a level group's `cursor`) instead of slicing what was fetched. - Every v3 surface is `bg-card-solid`: dark mode's glass rule restyled bare `bg-card` and dropped tile state edges, focus rings and the dashed no-reason chip. The levels sweep asserts no bare `bg-card` remains. - The node API seam matches api.md: client-only fields are optional with fallbacks (level key, tiles by default), `also_holds` and `why` may arrive as plain strings (`nodeWire.ts`), a root's level is its tree's root key, and the summary's badge flag is `try_ready` so `NodeFull.try_it` stays api.md's step list. Root counts carry their level names. - On sample data an answer's toast says nothing was answered. - Empty levels are filtered in the strip and the phone row, and the sweep now flags "0 later" and opens every level under every node. - `w` and the keys line follow what the page offers; roll-up separators end their part, so a wrapped phone line never starts with "·"; the strip names the node by its code (T-16 · …) and folds middle ancestors below 1024 px. - The sample engine loads in its own chunk, only when there is no node API. A task's v3 link on v1/v2 lands on its project; the person menu offers "Back to cockpit v2" while v3 is on. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
LittleChimera
force-pushed
the
factory/v3-web-shell
branch
from
November 3, 2026 20:11
1021aa1 to
2db1894
Compare
3 of 13 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Related issue
Cockpit v3 build plan (
notes/factory-v3/final/plan.md), web items W1 to W4. No GitHub issue.Summary
Cockpit v3 (the work levels) behind the cockpit choice:
?cockpit=v3puts the node pages inside the new cockpit's shell. v1 and v2 are unchanged, and v2 stays what someone who chose the new cockpit sees by default. The server has no node API yet, so the pages read a typed sample of the factory at 13:40 (same shape, neutral names). The page says it shows sample data. Server PR S4 swaps in the real endpoint by setting one preflight flag.ELI5: every page is one node of a strict tree. The page shows the node's ancestors and the levels below it in one row, then the node's header (what it is, why it exists, the one line that says who or what holds it), then its children as tiles or rows. Nothing is drawn between nodes. A link to another tree is a chip.
Plan bullets → what this PR does
W1. The v3 switch and routes
shell/cockpitV2.ts:CockpitChoicegains"v3", and?cockpit=v3is honoured while the server offers the new cockpit. The server stores only v1 or v2 (UiPrefs.cockpit, and this PR makes no server changes), so this browser keeps a v3 choice, and it beats the stored v1/v2 until the person picks v1 or v2 again.useCockpitV2()addsv3.onstays true under v3 because v3 is built on v2's shell, so v2's places (Teams, Steer, Debug…) still work.n/:refandn/:ref/:level(pages/factory/NodeRoute.tsx). Under v3,HomeRouterenders the product's node page. On v1 or v2, a v3 link goes to the closest page there: the project page, the PR page, else Home. The shell marks itselfdata-cockpit="v3", andshellRoutesknows thenodepage (presencenode:<ref>).useNode(ref)/useLevel(ref, level)/usePrefetchNode/useAnswerDecision(v3/nodeQueries.ts): TanStack Query, keyed by factory and ref, fresh for 5 s, prefetched on hover and focus of any node link.v3/nodeSource.ts): aNodeSourcewithnode,levelandanswer.liveNodeSourcereadsGET /v1/factory/factories/{fid}/n/{ref}and…/level/{level}and answers viaPOST /decisions/{id}/answer. It is used when preflight saysnode_api: true; otherwise the sample source is used. Stories and tests inject a source withNodeSourceProvider.v3/fixture/):sampleData.tsis the prototype's 13:40 data with neutral names.sampleEngine.tsports the prototype's engine and returns the api.md shapes: one parent per node, state as the worst active child, the blocker path (person-ending paths ranked by held weight), compact lines, why, chips, roll-ups, levels below, child pages with columns, and grouped level views.scaleSample.tsis the synthetic big product. Answers apply to the source's own copy, so Apply C-16 recomputes the whole page.Factory/Cockpit v3/Shell(v3 on, dark, phone, a node link, a level link, v3 off).W2. Header, status line, why card, chips
NodeHeader: at most five lines, then verbs. They are title · ref · level · kind · owner; why (safe markup,RichText); status; roll-up (ring, acceptance and build %, cost of budget, team "6 idle", base env, since, alarms closed); and chips. A line with nothing to say is left out.StatusLine:StatusBox(full path, label colour, "why? ▾", the answer) andCompactStatus(the end of the path only). The wording lives instatusModel.ts. The "the ask above" rule: a compact line that ends at the header's decision says "the ask above" and shows no button, and a ghost drops its options when the header asks the same thing.WhyCard(numbered, ends at the ask, the met-links note, "Start it now instead"),LinkChipandChipList(at most 3, "+n", count badges open lists grouped under breadcrumbs, warn tone for links with no reason).DecisionButtons: header (answer + "Open C-16"), tile (answer) and ghost (every option). Enter is swallowed on them, so an answer is always a click. External answers (merges a person makes) are links out.Factory/Cockpit v3/Headercovers moving, needs with a path, waits on (hop), held on purpose, not started for 7 h with met links (why open), done, later, proposed (ghost), root, and phone 390. Each comes in light and dark.W3. Level strip
LevelStrip: ancestors with state dots; folded implicit ancestors in italics with no dot; the node underlined; each level below with count, "+n later", "n you" and a state bar, linking ton/:ref/:level; "Below · nothing yet" when there is a next level but no node; the Results cell ("7 merged · all in base env", or "nothing merged yet").PhoneLevelRow: "‹ parent" and cells that scroll inside the row. Empty levels are omitted, and a level that only has later work says "4 later", never "0".Factory/Cockpit v3/Level stripcovers root, feature, folded project, project, task, run, factory tree, scale, and renamed levels (Outcome / Workstream from data), with phone variants.W4. Children: tiles, rows, columns, level view
NodeTile(ring or glyph, compact status, its answer only when the node itself needs you and the header doesn't ask it, grandchildren as up to 12 dots else a stacked bar, badges),NodeRow,OrderColumns(First / Then / … / Later (held), up to 4 to a row, done folded per column),DoneLine,LaterBox(reasons and note),LevelView(grouped under parents with their lineage, all-done groups folded into one line, rows paged at 60),ChildrenView(tiles ≤ 12, rows 13–60 with a note, grouped by state past 60). Acceptance and evidence render above the children when the node has them.useNodeKeys.ts): a roving tabindex over tile and row title links (one Tab stop; they are real links).j/kmove DOM focus, Enter orlstep in,hsteps out,wopens why. The keys yield togchords and to fields, menus and dialogs.KeyboardHelplists them under v3.Factory/Cockpit v3/Node pagecovers Home, Agent assistant, assistant-core tasks, scale tasks grouped (each in light, dark and phone), plus task, run, factory tree, and after Apply C-16.Deviations
(client)innodeTypes.tsand now innotes/factory-v3/final/api.mdtoo, so S4 builds to one contract. They put display names next to refs (segments and also-holds carrytitle),level_name/kind_name/glyph/tagon nodes and crumbs,detailandwhy_titleon status lines,whyas{html, tone},chips_more,homeon root pages,tiles/level_name/later_title/later_note/via_foldon child pages and level views, andnounon grandchild counts. Every one is optional: without them the page falls back to the level key, tiles, or the bare ref, andnodeWire.tsalso readsalso_holdsandwhyas api.md's plain strings. Rootcountsis a list of{key, name, plural, count, later?}so the nouns come from the definition.UiPrefs.cockpitisLiteral["v1","v2"]. A one-line server change can store v3 later.NodePagetakes anasidefor it.ui/ChainCountsis not reused as a component: the strip follows its count-plus-bar pattern, with colours fromui/statusTokens(MEANING). Moving work is blue and done work is green, as in the prototype.sample-org/sample-app, basesample-main, the competitors and the ads network renamed, and "you" in place of the owner's name. factory v2: scrub product names from factory fixtures and strings #194'sFORBIDDEN_SAMPLE_TOKENSratchet passes.Review fixes (second round)
grouped_by_state), proposals get a "Proposed" state group and held work keeps its Later box; nothing is dropped.NodeSource.nodeand.leveltake a cursor (?children_page=,?cursor=). Past the first page a "Next 60" footer readschildren.next(or a level group'snext) and appends the rows (useMoreChildren,useMoreInGroup, TanStack infinite queries that only fetch on the click).bg-card-solid. Dark mode's glass rule (index.css, top-level.bg-card) was replacing tile rings (the amber "needs you" edge,focus-withinrings), the dashed no-reason chip and the strip's current cell. Measured in Chromium: the needs tile's box-shadow keeps the amber 1px ring, and the warn chip isdashedamber. The levels sweep now asserts that no element on any v3 page carries a barebg-card..toLowerCase()on a missing name), a root'slevelis its tree's root key (product,fx) instead of arootsentinel, the summary's badge flag istry_readysoNodeFull.try_itstays api.md's step list, and "+n" chips show a count even withoutchips_more.nodeWire.test.tsxrenders sample pages stripped to api.md's bare shapes throughliveNodeSource.count + later == 0,LevelCountWordsrenders nothing for them, and the sweep's zero check now matches "0 later" / "0 you" and opens every level under every node (not only under the roots). A probe test injects an empty level.wis bound only when the header offers "why? ▾"; the keys line lists j/k/step in only with nodes to step into,honly with a parent,wonly with a why.import()that loads only under v3 withoutnode_api, so v1 and v2 do not ship it.T-nlink on v1/v2 lands on its project's page (from the tasks list), anI-nlink on factory: outcome-first initiatives — walking skeleton, acceptance runs on the base env, one progress view #205's initiative page; the person menu offers "Back to cockpit v2" whenever v3 is on.FactoryPage.tsx, both routes kept). No migration, no server change.Test Plan
npx vitest run src/components/factory src/pages/factory src/lib: 224 files, 4020 tests, all pass (after rebasing on factory: outcome-first initiatives — walking skeleton, acceptance runs on the base env, one progress view #205).v3/fixture/sampleEngine.test.ts: the api.md §8 contract cases on the sample. These are assistant-core "Not moving: T-16 → held for C-16 → needs you: Apply C-16 (D-45)" with no D-48; the panel's "Not started for 7 h: waits on assistant-core › T-16 → …" with the met-links note; the root's top blocker with "also holds"; Home grouped under needs; after Apply C-16 the feature ends at D-49; the implicit parent folding; and the one-parent check at both scales.v3/NodePage.test.tsx: one place per decision's answer on every sample page; Enter never answers (user-event and a raw keydown) while a click does; "the ask above"; why andw; the chip list; strip counts and "you" fromlevels_below, with no zero for a later-only level; folded ancestors; the phone row; columns, done line, later box and ghost options; grouping and paging at scale; the keyboard walk root → I-1 → assistant-core → T-16 → a097437 → run and back up withh; one roving tab stop.v3/levelsRule.test.tsx: no SVGline/path/polyline/polygon(and no canvas) on any v3 page, and no leaf cell that reads "0", "none" or "—". It runs on every node and every root level of the sample, on desktop and phone, after Apply with renamed levels, and on the synthetic big product.v3/Children.test.tsx(tiles ≤ 12, rows 13–60 with a note, state groups past 60 paged) andv3/statusModel.test.ts(wording rules).pages/factory/CockpitV3.test.tsx:?cockpit=v3turns v3 on without a PUT; v3 survives a reload over a stored v2; a/n/I-1link survives a reload;/n/product/projectopens the level; "Back to cockpit v2" saves v2 (from the page and from the person menu); v2 stays the default; a v3 link on v2 lands on the project page, a task's on its project, a feature's on its initiative; an answer on sample data says nothing was answered and posts no answer;?cockpit=v3is ignored when the server doesn't offer the new cockpit.Children.test.tsx(61 children with a ghost and held work; the server's "Next 60"),NodePage.test.tsx(paging through the cursor, renamed roll-up nouns,wand the keys line, the strip's code, the amber edge class, an injected empty level),levelsRule.test.tsx(every level under every node, "0 later" caught, no barebg-card),nodeWire.test.tsx(api.md's bare shapes through the live source; cursor URLs).tsc -b,oxlint --deny-warnings,prettier --check,pre-commiton the changed files, andtests/examples/test_factory_sample_tokens.py: all pass.To try it: open any factory with
?cockpit=v3while the server offers the new cockpit (OMNIGENT_FACTORY_COCKPIT_V2=1). Home becomes the product's node page (sample data). Then:jand Enter to step in, andhto step out;Demo
Side-by-side page, prototype shots against this PR's Storybook captures at 1440 and 390, light and dark, with what matches and what differs for each: https://claude.ai/artifact/BDSmQkGgybAKRBTi8EX1dv (private; ask Luka for access). Screenshots are not committed (repo rule), and the CLI cannot upload attachments. The captures sit locally in
wt/v3-tmp/v3-web-shell/shots/.Second-round captures (dark Home and Agent assistant with the amber edges back, the phone roll-up, the strip at 800 px) are in
wt/v3-tmp/v3-web-shell-fix/shots/.Stories to open:
Factory/Cockpit v3/Node page(Home, AgentAssistant, AssistantCoreTasks, ScaleTasksGrouped, AfterApplyC16, with Dark and Phone variants),Factory/Cockpit v3/Header,Factory/Cockpit v3/Level strip,Factory/Cockpit v3/Shell.Type of change
Test coverage
Coverage notes
Manual: every sample page was swept in Chromium at 390 and 1440 for horizontal overflow and SVG lines. Twelve screens were captured and compared with the prototype's shots (Demo link). The Playwright end-to-end checks are W11.
Changelog
Cockpit v3 preview behind
?cockpit=v3: the factory as levels, with one node per page, its status line and its children.🤖 Generated with Claude Code