Repository navigation
feat(conductors): Mind + Cortex dashboards emit the state.json cockpit feed #418
Description
Overview
Brain, Heart, Hands and Memory now publish the v1 cockpit feed. The Mind task dashboard and the Cortex
science dashboard are different: their pages are rendered by Brain conductors (intake _intake.py
dashboard --apply writes PyAutoMind/dashboard.{md,html}; cortex _cortex.py dashboard/checkin
writes PyAutoCortex/dashboard.{md,html}), committed into those repos, and their pages_dashboard.yml
only copies dashboard.html into _site. So the emit lives in the Brain, and the two repos need the
file allowed through their guards and copied/validated in their workflows.
Both conductors already put PyAutoBrain/board/ on sys.path (intake ~L62, cortex L71), so
from _state import build_state, validate_state works with no new path hack (same as board/_board.py).
Design decisions
updatedvs--checkidempotence.census()["generated"]is date-only in both conductors and the
contract needs a UTC time. Emit the real render time (datetime.now(UTC)→ whole-second Z) and make
the drift check comparestate.jsonwithupdatednormalised out (a_state_body()that loads the
JSON, dropsupdated, re-dumps), exactly as_dashboard_bodydrops the generation stamp.- Never re-derive. Items carry the same copy payloads the dashboard rows already carry
(/start_dev <path>,_registry_payload,_review_payload,checkin_payload,resume_payload). - Merge order. Mind and Cortex PRs first (guards + workflows, harmless with the file absent:
cp
guarded by[ -f state.json ]), then Brain (starts emitting). Otherwise the Mind self-heal would
commit an unlisted root file andspawn_drift.yml/ the ledger merge would go red.
PR A — PyAutoMind (feature/mind-cortex-state-feed)
scripts/spawn.pyMIND_RULES(~L113-118): add("state.json", "DROP")besidedashboard.html;
mirror indocs/pyautobrain/spawn_spec.md(rows 7b/7c).tests/test_spawn_template_contract.py
keeps guarding.scripts/ledger_merge.py: addstate.jsontoLEDGER_FILES(L80-92) andGENERATED_FILES(L249);
extendtests/test_ledger_merge.py(a claude/** branch carrying it is still auto-mergeable; a conflict
resolves to main's copy)..github/workflows/mind_ledger_merge.yml(~L245-248): addstate.jsonto the explicitgit add -A --
list after the--apply dashboardre-render..github/workflows/dashboard_refresh.yml: trigger paths + thegit add dashboard.md dashboard.html
(L237) gainstate.json; the freshness--checkalready covers it once Brain's check does..github/workflows/pages_dashboard.yml: trigger path +[ -f state.json ] && cp state.json _site/state.json;
new step checking outPyAutoLabs/PyAutoBrain(pathPyAutoBrain, likedashboard_refresh.yml) and
running[ -f _site/state.json ] && python3 PyAutoBrain/board/_state.py _site/state.json("Validate the
cockpit feed against the Brain contract").
PR B — PyAutoCortex (feature/mind-cortex-state-feed)
scripts/ledger_merge.pyLEDGER_FILES(L79) +=state.json(socortex checkin --pushand the
ledger auto-merge accept it);tests/test_ledger_merge.pycase..github/workflows/dashboard_refresh.ymlgit add(L158) + trigger paths +=state.json..github/workflows/pages_dashboard.yml: same guardedcp+ Brain checkout + validate step as Mind..github/workflows/cortex_check.ymlpaths: optional, addstate.jsonfor symmetry.
PR C — PyAutoBrain (feature/mind-cortex-state-feed, after A and B merge)
agents/conductors/intake/_intake.py:render_state(c)→ JSON text viabuild_state(organ="mind", repo=<from c["home"]>, status, headline, updated, pages_url=_pages_url(c["home"]), items).
Status: red ifc["drift"]orc["hygiene"]flags block, or any in-flight row's status contains
awaiting-input/blocked; yellow if any in-flight row isawaiting-merge/library-shipped, or
human_reviewnon-empty, orpending_releasenon-empty, or picks exist; green otherwise;
grey when the census has no records at all. Headline = the Start-here count line the page shows
(N picks · M in flight), state-word prefixed when not green. Items (cap 20, red→yellow→info):
in-flight rows needing a human (url = PR if any else issue, prompt = the row's own payload); human
review rows (_review_payload); pending-release rows; then top picks (/start_dev {path}, info).
pagesdict gains"state.json";--checkuses_state_bodyfor that key;--applywrites it.agents/conductors/cortex/_cortex.py:render_state(c)for organcortex: red ifc["problems"];
yellow ifcounts.running > 0or the check-in is stale (port the JS freshness rule at L583-600 to
Python, one helper) ; green otherwise; grey with no ledgers. HeadlineA active · R running.
Items: problems → red (url = ledger issue_url if any); stale check-in → yellow withcheckin_payload;
running runs → info withresume_payload(key,row)and the ledger's issue_url.render_pagesadds
state.json;dashboard_bodyequivalent normalisesupdated;cmd_dashboardcheck/apply and
cmd_checkinwrite it;classify_pathswill pass once PR B is on Cortex main.- Tests:
tests/test_intake_dashboard.py—--applywrites a state.json that validates on the fictional
Mind; awaiting-merge row → yellow item with the PR url; empty Mind → grey;--checkis clean after
--applyeven whenupdatedchanges (monkeypatch the clock).tests/test_cortex_conductor.py— same
on the skeleton (grey/green, a problem → red, a running run → info with resume payload). Fixtures
tests/fixtures/state/{mind,cortex}.jsonjointest_state_feed.py's parametrised validation. - Docs: one line each in the two conductors'
AGENTS.mdandboard/AGENTS.md's feed list.
Branch survey / claims
| Repo | Branch | Dirty | Claimed by |
|---|---|---|---|
| PyAutoBrain | main | clean | eyes-organ-order (AGENTS/README/docs/root-resolution), cosmos-web-ring-greeting (clone conductor) — disjoint |
| PyAutoMind | main | clean | eyes-organ-order (repos.yaml, hooks, scripts/repos_sync.py) — disjoint |
| PyAutoCortex | claude/checkin-2026-09-19, 7 dirty, 5 unpushed (a science check-in session) |
— | eyes-organ-order (AGENTS.md) — disjoint |
The canonical Cortex checkout is left untouched; the task worktree cuts from origin/main. Own worktree
with parallel-claim: recorded (the #177 precedent).
Execution (Fable session)
/create_issue on PyAutoBrain → register active.md (parallel-claim) → worktree_create mind-cortex-state-feed PyAutoBrain PyAutoMind PyAutoCortex → ONE Opus subagent (three repos, three
commits, Mind/Cortex first so the Brain renderers can be exercised against them locally), progress file +
Monitor → /ship_library: Heart gate (RED, integrate — ask again), push + PRs A, B, C; merge A and B on
green with the human's command, then C; dispatch dashboard_refresh.yml on Mind and Cortex (they
re-render, commit state.json, dispatch Pages) → verify both published feeds → close-out.
Verification
- Brain:
pytest tests/test_intake_dashboard.py tests/test_cortex_conductor.py tests/test_state_feed.py;
_intake.py --mind <wt>/PyAutoMind --apply dashboardthendashboard --checkclean; the written
PyAutoMind/state.jsonpassespython board/_state.py; same for_cortex.py --cortex <wt>/PyAutoCortex. - Mind:
pytest tests/test_spawn_template_contract.py tests/test_ledger_merge.py;spawn.py --checkOK. - Cortex:
pytest tests/test_ledger_merge.py. - After merges: both
dashboard_refresh.ymlruns commitstate.jsonand dispatch Pages; both
pages_dashboard.ymlruns green withstate: ok;curl https://pyautolabs.github.io/{PyAutoMind,PyAutoCortex}/state.jsonvalidates.
Original Prompt
Click to expand starting prompt
Organ cockpit: Mind and Cortex dashboards emit state.json via their Brain renderers
Type: feature
Target: PyAutoBrain
Repos:
- PyAutoBrain
- PyAutoMind
- PyAutoCortex
Difficulty: medium
Autonomy: safe
Priority: high
Status: formalised
Consequence: notify
Witness: pyauto-brain intake dashboard --check reports current with state.json tracked in PyAutoMind; both pages_dashboard.yml runs green with 'state: ok'; curl https://pyautolabs.github.io/PyAutoMind/state.json and /PyAutoCortex/state.json pass the validator.
Review-minutes: 0
Unattended: ready
Filed: 2026-09-26
Epic: organ-cockpit
Phase 1 (PyAutoBrain#416, shipped 2026-09-26) defined the per-organ state.json v1 cockpit feed (board/state_schema.json, board/_state.py). The Mind task dashboard and the Cortex science dashboard are not rendered in their own repos: PyAutoBrain's intake conductor writes PyAutoMind/dashboard.md + dashboard.html (pyauto-brain intake --apply dashboard) and the cortex conductor writes PyAutoCortex's dashboard, and each repo's pages_dashboard.yml only copies dashboard.html into _site. So the emit lives in the Brain renderers and the publish/validate lives in the two workflows.
- PyAutoBrain intake dashboard renderer: --apply also writes PyAutoMind/state.json (organ mind, repo PyAutoMind, status from the dashboard's own headline logic — red if any task is blocked/awaiting-input, yellow if picks are waiting, green otherwise; headline = the picks/in-flight count line; items = in-flight tasks awaiting a human (awaiting-merge, awaiting-input, human review) each with its issue/PR link and the existing /start_dev or /prm copy payload as prompt). Validate through board/_state.validate_state before writing. The dashboard --check must also compare state.json.
- PyAutoBrain cortex conductor: the same for PyAutoCortex/state.json (organ cortex: red if any project ledger reports a failed run, yellow if runs are pending pull/checkin, green otherwise; items = projects needing the human with links).
- PyAutoMind and PyAutoCortex pages_dashboard.yml: cp state.json _site/state.json and a validate step; both jobs must check out PyAutoBrain (like knowledge_board.yml does) to run python3 PyAutoBrain/board/_state.py _site/state.json. dashboard_refresh.yml self-heal must regenerate state.json with the html.
- Brain tests: both renderers produce a feed that validates on the hermetic dashboard fixtures; Mind/Cortex commit guards accept the new tracked file.
Witness: pyauto-brain intake dashboard --check reports current with state.json tracked in PyAutoMind; both pages_dashboard.yml runs green with 'state: ok'; curl https://pyautolabs.github.io/PyAutoMind/state.json and /PyAutoCortex/state.json pass the validator.
🤖 Generated with Claude Code
Activity
Jammy2211 commentedon Sep 26, 2026
PRs created (3) — under Heart RED override
render_state,--checkcovers state.json withupdatednormalised; includes the_mind_homeregex fix — the Mind dashboard had lost its Pages pointer since the grouped-checkouts layout)Merge order: A → B → C. Both worktree-rendered feeds validate (
state: ok): Mind yellow (24 picks · 7 in flight, 20 items), Cortex yellow (7 active · 7 running, 8 items). Tenant firewall OK.Heart RED override (development only). Heart readiness RED (snapshot 2026-09-26T11:27:07+00:00); verbatim reason:
release validation FAILED (stage integrate)— pre-existing, unrelated. Human authorization, live, 2026-09-26, this session: "override and continue" (development shipping + merge on green checks; no release).Merge pull request #420 from PyAutoLabs/feature/mind-cortex-state-feed
Jammy2211 commentedon Sep 26, 2026
Shipped
dashboard_refresh.ymlself-heals (Mind 36242390977, Cortex 36242392736) committedstate.jsonto main and dispatched Pages; bothpages_dashboard.ymlruns green withstate: ok(Mind 36242402969, Cortex 36242404798).python board/_state.py:yellow | 24 picks · 6 in flight | 20 itemsyellow | 7 active · 7 running | 8 items_mind_homeregex fix restored the Mind dashboard's Pages pointer and absolute links (the self-heal committed that diff with the feed).Six organs now publish the cockpit feed (Brain, Heart, Hands, Memory, Mind, Cortex). Remaining: Eyes, Gut, Nerves (no board yet); then the PWA cockpit page, the start_dev Heart-RED gate, tray dot, status line.