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
updated vs --check idempotence. 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 compare state.json with updated normalised out (a _state_body() that loads the
JSON, drops updated, re-dumps), exactly as _dashboard_body drops 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 and spawn_drift.yml / the ledger merge would go red.
PR A — PyAutoMind (feature/mind-cortex-state-feed)
scripts/spawn.py MIND_RULES (~L113-118): add ("state.json", "DROP") beside dashboard.html;
mirror in docs/pyautobrain/spawn_spec.md (rows 7b/7c). tests/test_spawn_template_contract.py
keeps guarding.
scripts/ledger_merge.py: add state.json to LEDGER_FILES (L80-92) and GENERATED_FILES (L249);
extend tests/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): add state.json to the explicit git add -A --
list after the --apply dashboard re-render.
.github/workflows/dashboard_refresh.yml: trigger paths + the git add dashboard.md dashboard.html
(L237) gain state.json; the freshness --check already 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 out PyAutoLabs/PyAutoBrain (path PyAutoBrain, like dashboard_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.py LEDGER_FILES (L79) += state.json (so cortex checkin --push and the
ledger auto-merge accept it); tests/test_ledger_merge.py case.
.github/workflows/dashboard_refresh.yml git add (L158) + trigger paths += state.json.
.github/workflows/pages_dashboard.yml: same guarded cp + Brain checkout + validate step as Mind.
.github/workflows/cortex_check.yml paths: optional, add state.json for symmetry.
PR C — PyAutoBrain (feature/mind-cortex-state-feed, after A and B merge)
agents/conductors/intake/_intake.py: render_state(c) → JSON text via build_state(organ="mind", repo=<from c["home"]>, status, headline, updated, pages_url=_pages_url(c["home"]), items).
Status: red if c["drift"] or c["hygiene"] flags block, or any in-flight row's status contains
awaiting-input/blocked; yellow if any in-flight row is awaiting-merge/library-shipped, or
human_review non-empty, or pending_release non-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).
pages dict gains "state.json"; --check uses _state_body for that key; --apply writes it.
agents/conductors/cortex/_cortex.py: render_state(c) for organ cortex: red if c["problems"];
yellow if counts.running > 0 or the check-in is stale (port the JS freshness rule at L583-600 to
Python, one helper) ; green otherwise; grey with no ledgers. Headline A active · R running.
Items: problems → red (url = ledger issue_url if any); stale check-in → yellow with checkin_payload;
running runs → info with resume_payload(key,row) and the ledger's issue_url. render_pages adds
state.json; dashboard_body equivalent normalises updated; cmd_dashboard check/apply and
cmd_checkin write it; classify_paths will pass once PR B is on Cortex main.
- Tests:
tests/test_intake_dashboard.py — --apply writes a state.json that validates on the fictional
Mind; awaiting-merge row → yellow item with the PR url; empty Mind → grey; --check is clean after
--apply even when updated changes (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}.json join test_state_feed.py's parametrised validation.
- Docs: one line each in the two conductors'
AGENTS.md and board/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 dashboard then dashboard --check clean; the written
PyAutoMind/state.json passes python 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 --check OK.
- Cortex:
pytest tests/test_ledger_merge.py.
- After merges: both
dashboard_refresh.yml runs commit state.json and dispatch Pages; both
pages_dashboard.yml runs green with state: ok; curl https://pyautolabs.github.io/{PyAutoMind,PyAutoCortex}/state.json validates.
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
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.pydashboard --applywritesPyAutoMind/dashboard.{md,html}; cortex_cortex.pydashboard/checkinwrites
PyAutoCortex/dashboard.{md,html}), committed into those repos, and theirpages_dashboard.ymlonly copies
dashboard.htmlinto_site. So the emit lives in the Brain, and the two repos need thefile allowed through their guards and copied/validated in their workflows.
Both conductors already put
PyAutoBrain/board/onsys.path(intake ~L62, cortex L71), sofrom _state import build_state, validate_stateworks with no new path hack (same asboard/_board.py).Design decisions
updatedvs--checkidempotence.census()["generated"]is date-only in both conductors and thecontract needs a UTC time. Emit the real render time (
datetime.now(UTC)→ whole-second Z) and makethe drift check compare
state.jsonwithupdatednormalised out (a_state_body()that loads theJSON, drops
updated, re-dumps), exactly as_dashboard_bodydrops the generation stamp.(
/start_dev <path>,_registry_payload,_review_payload,checkin_payload,resume_payload).cpguarded by
[ -f state.json ]), then Brain (starts emitting). Otherwise the Mind self-heal wouldcommit an unlisted root file and
spawn_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 in
docs/pyautobrain/spawn_spec.md(rows 7b/7c).tests/test_spawn_template_contract.pykeeps guarding.
scripts/ledger_merge.py: addstate.jsontoLEDGER_FILES(L80-92) andGENERATED_FILES(L249);extend
tests/test_ledger_merge.py(a claude/** branch carrying it is still auto-mergeable; a conflictresolves 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) gain
state.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 out
PyAutoLabs/PyAutoBrain(pathPyAutoBrain, likedashboard_refresh.yml) andrunning
[ -f _site/state.json ] && python3 PyAutoBrain/board/_state.py _site/state.json("Validate thecockpit feed against the Brain contract").
PR B — PyAutoCortex (
feature/mind-cortex-state-feed)scripts/ledger_merge.pyLEDGER_FILES(L79) +=state.json(socortex checkin --pushand theledger 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 if
c["drift"]orc["hygiene"]flags block, or any in-flight row's status containsawaiting-input/blocked; yellow if any in-flight row isawaiting-merge/library-shipped, orhuman_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 if
counts.running > 0or the check-in is stale (port the JS freshness rule at L583-600 toPython, one helper) ; green otherwise; grey with no ledgers. Headline
A active · R running.Items: problems → red (url = ledger issue_url if any); stale check-in → yellow with
checkin_payload;running runs → info with
resume_payload(key,row)and the ledger's issue_url.render_pagesaddsstate.json;dashboard_bodyequivalent normalisesupdated;cmd_dashboardcheck/apply andcmd_checkinwrite it;classify_pathswill pass once PR B is on Cortex main.tests/test_intake_dashboard.py—--applywrites a state.json that validates on the fictionalMind; 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— sameon 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.AGENTS.mdandboard/AGENTS.md's feed list.Branch survey / claims
claude/checkin-2026-09-19, 7 dirty, 5 unpushed (a science check-in session)The canonical Cortex checkout is left untouched; the task worktree cuts from
origin/main. Own worktreewith
parallel-claim:recorded (the #177 precedent).Execution (Fable session)
/create_issueon PyAutoBrain → register active.md (parallel-claim) →worktree_create mind-cortex-state-feed PyAutoBrain PyAutoMind PyAutoCortex→ ONE Opus subagent (three repos, threecommits, 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 ongreen with the human's command, then C; dispatch
dashboard_refresh.ymlon Mind and Cortex (theyre-render, commit
state.json, dispatch Pages) → verify both published feeds → close-out.Verification
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 writtenPyAutoMind/state.jsonpassespython board/_state.py; same for_cortex.py --cortex <wt>/PyAutoCortex.pytest tests/test_spawn_template_contract.py tests/test_ledger_merge.py;spawn.py --checkOK.pytest tests/test_ledger_merge.py.dashboard_refresh.ymlruns commitstate.jsonand dispatch Pages; bothpages_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:
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.
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