Repository navigation
cockpit v3: the real node API end to end (Home, the feature, the project and the task on a live server) - #211
Merged
Merged
Conversation
Cockpit v3 reads the server's node API by default; the sample factory is
for Storybook and the web tests only (the OMNIGENT_FACTORY_NODE_API gate
and preflight node_api are gone).
Seams fixed against a server seeded like a real factory:
- Home lists the features under their needs: GET …/n/{ref}/level/{level}
is served (grouped under parents, all-done groups folded, 60 a group).
- Apply answered with the ref (D-45), which the answer route 404s: a
DecisionRef now carries the decision's row_id and the live source posts it.
- Proposal decisions are stored as blocking nothing, so real "needs you"
lines and the root's top blocker ignored them: the tree counts a
proposal's decision as holding work while it adds a project or a rule,
or changes a project that is still active.
- A task nothing holds, and the base env's setup project, now end at who
starts them instead of a bare "Not started".
- Results leave out the in-base-env count when there is no base env.
tests/e2e_ui/factory_v3 seeds a 13:40-shaped factory into a running
server and checks Home, the feature, the project and the task at 1440 and
390, light and dark (no console error, no line between nodes, no zero or
"none" cell, a status line ending at the person), Apply through the real
answer route, the chat column's thread, and the keyboard walk.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
After Apply on D-45 released T-16, assistant-core's line read a bare
"Not started" and assistant-panel-ux's ended at "waits on assistant-core":
nothing blocked under the project, so the path found no step. A node with
nothing blocked under it now steps down to the child that has not started
("Not started: T-16 → waits for Juno to start it"), and a task in a
project nobody has started hops to who starts that project instead of
"its lead". The e2e Apply test checks the project, the panel and Home
after the answer, and the render test covers the level views.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
❌ UI Snapshot doesn't match the committed baseline. If this UI change is intentional, update the baseline — each path renders in the same pinned image, so the result matches this gate:
Diff PNGs ( |
This was referenced Nov 4, 2026
Merged
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
Closes # (cockpit v3, milestone 1 "Read-only v3":
notes/factory-v3/final/plan.md§3, the integration of S1–S7 and W1–W5 on the real API, plus the first bullet of S8 and a first slice of W11)Summary
Milestone 1 is done when Luka opens his live factory with
?cockpit=v3and sees real data. Until now?cockpit=v3showed a built-in sample unless an operator setOMNIGENT_FACTORY_NODE_API=1. With the gate on, the real pages still had gaps: Home had no features, Apply returned 404, and the status lines skipped the ask a person really owed.This PR:
v1 and v2 are unchanged. v2 stays the default cockpit.
ELI5: the new cockpit used to show a pretend factory. It now asks the server for the real one. Running it against a realistic factory showed five places where the page and the server did not fit. Each is fixed, and a robot browser now clicks through the pages on every PR.
Plan bullet → what this PR does
?cockpit=v3; v2 stays the default"useNodeSourcereturns the provider's source, elseliveNodeSource. There is no preflight gate and no sample fallback in the live path, and the sample chunk is no longer in the production bundle.OMNIGENT_FACTORY_NODE_APIand preflightnode_apiare removed. The page shows the "Sample data" notice only for a sample source a story or test passes in.useNode/useLevelread the serveruseLevelhad no server route behind it, so Home (home: "feature") rendered no features. S8, first bullet:GET …/n/{ref}/level/{level}. It groups the nodes on that level under their parent (a section title, never a node joined by lines) and folds groups whose work is all done (done_groupswhen more than one). Each group pages at 60, with anextcursor per group (<parent ref>@<offset>). It returns 404unknown_levelwhen the level is not below the node and 400bad_cursorfor a bad cursor. It ports the sample engine'slevel()and is validated bynode_views.LevelViewView.DecisionRef.idis the ref (D-45), so every Apply returned 404unknown_decision.DecisionRefnow carriesrow_id, and the live source posts it.blocking=False(proposals._mirror), while the 13:40 fixture had marked them blocking. On real data, D-45 therefore neither coloured assistant-core nor ranked as a person-ending path, and the root's top blocker went to the merges.Tree.blocks(decision)fixes this. A proposal's decision holds work while the proposal adds a project (create or split), adds a rule (tuning, rules or code), or changes a project that is still active. C-14 and C-15, which change finished projects, still block nothing. The fixture now stores decisions the way production does.Results.in_base_envis now absent when the server knows no deployed commit, and the strip then reads "7 merged".factory, a project toproject:<slug>, and a task to its project's thread withAbout T-16 · Debit each turn: …(per-node threads are S11).j/kto a tile,l/↵to step in, from Home to I-1, assistant-core and T-16.hsteps back out through the need to Home.tests/e2e_ui/factory_v3(below) runs in the existinge2e-uiworkflow (uv run pytest tests/e2e_ui).The e2e test and its seed
tests/e2e_ui/factory_v3/seed.pywrites a factory shaped like the 13:40 one into a running server's database, using the server's own SQL stores. It uses the same story asnode_fixtures.factory_1340, with neutral names and every time relative to now:main;POST …/proposals, so the answer route really applies them. It can also seed a local server by hand (python -m tests.e2e_ui.factory_v3.seed --db … --base-url … --owner …).test_v3_live_api.pyruns against one server per module: single-user, factory cockpit offered, supervisor off, a fresh factory per test.svg line/path/polyline/polygonin the page body. It must have no leaf reading0,noneor—, and no sideways overflow. The status line must havedata-kind="needs", read "needs you: Apply C-16 (D-45)", and carry its Apply button. Home must show the level view's groups.POST /v1/factory/decisions/{row_id}/answer(200) and shows the toast "D-45: Apply". The line stops ending at D-45, the API says needs 0, and T-16 now reads "Not started: waits for … to start it".factory,project:assistant-core, andproject:assistant-corewith the "About T-16 · Debit each turn:" prefix.Deviations
extra_http_headersdo not reach the/v1/sessions/updatesWebSocket, and its 403 fails the console check. That is a quirk of the test harness; production uses a proxy, which sets the header on every request. The node API, the answer route and the chat are the same code in both modes.cursoronly.group,lens,stateandq(api.md §3.6) are not read yet; nothing in v3 sends them.shots/. The full screen set is W11 (milestone 3).notes/factory-v3/final/api.mddocumentsDecisionRef.row_id, the optionalResults.in_base_env, the served level route and the proposal-blocking rule.Test Plan
uv run --frozen pytest -q -n 8 tests/server/factory tests/examples/test_factory_sample_tokens.py tests/stores/test_factory_store.py: 4383 passed.row_id, and clears once answered through the route.npm run type-check,npm run lintandnpm run format:checkare clean.vitest run src/components/factory src/pages/factory src/lib: 4094 passed.nodeLevels.server.json). It checks that Home lists the features from it, that Apply posts the row id, and the results words.CockpitV3.test.tsxserves the server's own pages:?cockpit=v3reads/n/productand/n/product/level/featurewith no sample notice. Apply posts/decisions/<row_id>/answerand the page reads again.CI=1 uv run pytest tests/e2e_ui/factory_v3 --ui-skip-build --browser-channel chrome: 7 passed (20 s), run twice on the final head.pre-commit run --files <changed>: everything passes except pyrefly's 28 errors. All 28 were already on staging (runtime/telemetry.py,onboarding/sandboxes/gensee.py,background/reconcile.py), and none is in a touched file. TheFORBIDDEN_SAMPLE_TOKENSratchet is green and now also scanstests/e2e_ui/factory_v3.fx,fx:health,fx:rulesandbase-env, at 1440 light and 390 dark;To try it: deploy, then open
https://<server>/factory/<id>?cockpit=v3(no env var needed). Home lists your features under the ask they came from. The status line ends at the ask you owe, and Apply on it answers that ask for real. To try it locally, startomnigent serverwithOMNIGENT_FEATURES=factory OMNIGENT_FACTORY_COCKPIT_V2=1and runuv run python -m tests.e2e_ui.factory_v3.seed --db sqlite:///<its db> --base-url http://127.0.0.1:<port> --owner local. The seed prints the URL to open.Demo
The screenshots from the manual run (Home, I-1, assistant-core and T-16 at 1440 light and 390 dark) are local captures. Uploading GitHub attachments is not available from this CLI session, so they are not attached; per CLAUDE.md they are not committed either. I can post them if someone uploads them, or the e2e above shows the same pages.
Type of change
Test coverage
Coverage notes
Manual: a local server seeded with
seed.py, checked in the browser at 1440 light and 390 dark across 17 node pages, and Apply clicked through to the real answer route. Not covered: the live factory itself (its data is not touched from here), and the pixel comparison with the prototype's screenshots (W11).Changelog
Cockpit v3 (
?cockpit=v3) shows your real factory: features grouped under the asks they came from, status lines that end at the ask you owe, and Apply that answers it.🤖 Generated with Claude Code