Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,7 @@ the `/<verb>` slash commands.
| `workspace` | The voice — the organism's expressive function: plan/survey workspace + HowTo example authorship (workspace\|howto registers) — WorkspaceDecision (never writes) | `bin/pyauto-brain workspace` |
| `eyes` | The perceptive function — the organism's sense of its own appearance: survey/review a visualization workspace's figure surface, critiques route to intake/start_dev — EyesSurvey/EyesReviewSurface (never renders, never edits) | `bin/pyauto-brain eyes` |
| `profiling` | The proprioceptive function — the organism's sense of its own effort: campaign/ingest/triage plans over the autolens_profiling workspace — ProfilingDecision | `bin/pyauto-brain profiling` |
| `cortex` | The learning function — where the organism finds out what is true: the science check-in over PyAutoCortex ('cortex checkin') — pull every active project through its own sync CLI, score every live run against its pre-registered witness, move what came back to awaiting-ruling, re-render the board and summarise it by project; never submits a run, never writes a ruling | `bin/pyauto-brain cortex` |
| `cortex` | The learning function — where the organism finds out what is true: the science check-in over PyAutoCortex ('cortex checkin') — pull every active project through its own sync CLI, score every live run against its pre-registered witness, move what came back to awaiting-ruling, re-render the board and summarise it by project; its door never submits a run and never writes a ruling; the agent submits only on the human's ask, and the ruling is the human's word | `bin/pyauto-brain cortex` |
| `hygiene` | The maintenance function — the organism's sense of its own upkeep: code-quality debt (dev-loop cost + tidiness), delegating fixes — HygieneDecision | `bin/pyauto-brain hygiene` |
| `clone` | The Mitosis Agent: partition the reference assistant, analyze the domain, emit the CloneDecision; --apply --mode lightweight-seed delegates the seed birth to Build; 'clone sync' patches the reference's generic-file diff onto the born siblings (dry run by default; rejected hunks are a human decision) | `bin/pyauto-brain clone` |
| `build` | Coordinate execution: consult the vitals faculty, then delegate to PyAutoHands | `bin/pyauto-brain build` |
Expand Down
7 changes: 4 additions & 3 deletions AUTONOMY.md
Original file line number Diff line number Diff line change
Expand Up @@ -407,9 +407,10 @@ for a science one (`batch plan --kind cortex` writes it). The two surfaces hold
separate records with separate `review-at:`s, so a member that read the other
organ's would be reading another shift's grant. The "no record → solo `--auto`"
rule above is unchanged for dev members and **inapplicable to a cortex member**:
a science member is never `--auto` — the human submits the run and rules on it,
so there is no autonomous acknowledgement for a grant to license. A Cortex record
therefore carries no `heart-ack:` and no `expected-effects:` at all (the Cortex's
a science member is never `--auto` — a run is submitted only on the human's
ask and the human rules on it, so there is no autonomous acknowledgement for a
grant to license. A Cortex record therefore carries no `heart-ack:` and no
`expected-effects:` at all (the Cortex's
own `batches/AGENTS.md` drops both: the Heart gates releases, not runs), and
**their absence there is never read as an unacknowledged RED** — the fields are
not missing, they do not exist on that schema.
Expand Down
86 changes: 49 additions & 37 deletions agents/conductors/cortex/AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,14 @@

> **Tier: conductor** — a front-door agent you *drive*. The *learning
> function* — where the organism finds out what is true: it reasons over
> PyAutoCortex (the science body map, the pre-registered phases, the rulings
> PyAutoCortex (the science body map, the pre-registered tasks, the rulings
> of record) and answers one question — *where is my science?* — with one
> command, `checkin`: pull every active project, score every live run, move
> what came back, re-render the board, push the ledger, summarise by project.
> It **never submits a run and never writes a ruling**: the run is the human's
> act, the verdict is the human's word, and the ruling file is the Cortex
> script's to write.
> Its door **never submits a run and never writes a ruling**: a run is
> submitted only on the human's ask (by the human, or by the agent in the
> session through the project's own sync CLI), the verdict is the human's
> word, and the ruling file is the Cortex script's to write.

The same split the organism already uses twice — **Heart ↔ vitals**, **Gut ↔
hygiene**: the organ keeps the state, the conductor reasons over it. The
Expand All @@ -20,11 +21,11 @@ the Cortex's intake-and-dashboard conductor.
| Verb | Question | Emits |
|------|----------|-------|
| `checkin [--dry-run \| --apply] [--push \| --no-push] [--project KEY] [--skip-pull] [--refreshed ISO]` | **Where is my science?** | the door — the sequence below, ending in a summary keyed by project; exit **1** when a pull failed or the tree needs a look |
| `census` | What is the Cortex holding? | phase counts by state, the board's section counts, rulings, projects, the last check-in stamp (`--json` for the lot) |
| `census` | What is the Cortex holding? | task counts by state, the board's section counts, rulings, projects, the last check-in stamp (`--json` for the lot) |
| `dashboard --check` | Are the committed pages current? | exit 0 current · **1 stale** · 2 no checkout · 3 unreadable tree |
| `dashboard --apply` | — | writes `dashboard.md` + `dashboard.html` |
| `gates` | What is each gated phase waiting on? | every gated phase, its refs and the URL each resolves to — read-only and offline |
| `collect [--pull] [--refreshed ISO] [--apply] [--out F] [--phase REL]` | What came back, and is it worth reviewing? | one block per phase — six scored legs, the readout, a blank ruling line — and exit **1** when any is not HEALTHY. **Default scope: every `submitted \| running` phase**; `--phase REL` narrows |
| `gates` | What is each gated task waiting on? | every gated task, its refs and the URL each resolves to — read-only and offline |
| `collect [--pull] [--refreshed ISO] [--apply] [--out F] [--task REL]` | What came back, and is it worth reviewing? | one block per task — six scored legs, the readout, a blank ruling line — and exit **1** when any is not HEALTHY. **Default scope: every `submitted \| running` task**; `--task REL` narrows |

```
pyauto-brain cortex checkin --dry-run # what it would pull and score
Expand All @@ -46,8 +47,8 @@ Mind's: a session holding one organ and not the other still works.
## What it reads, and what it refuses to read

- `<cortex_root>/scripts/cortex.py` is imported at runtime and is the schema
API: `load_phases`, `load_rulings`, `load_projects`, `gates_report`,
`move_phase`. The conductor always reasons with the schema
API: `load_tasks`, `load_rulings`, `load_projects`, `gates_report`,
`move_task`. The conductor always reasons with the schema
the checkout it is pointed at implements.
- **Stdlib only, and Mind-free.** The renderer runs bare inside the Cortex's
own `dashboard_refresh.yml`, which installs nothing and checks out no
Expand All @@ -63,19 +64,30 @@ Mind's: a session holding one organ and not the other still works.
The page opens on the door, not on the work: a counts table (what
`board/_board.py` reads for the Brain board's Cortex strip), then the
**check-in chip** — the paste for the laptop's science chat — and the **last
check-in** stamp under it. Then **Summary** (one row per active project),
then the sections in the reading order of a check-in: **Projects** (active →
planned → dormant; each card is the folders, the counts and the ONE phase to
act on next, with the rest of its open phases and its issue list behind a
fold) → **Awaiting ruling** (ordered failures → a ruling is required → clean)
→ **Running / submitted** → **Ready** (one visible row per project, the
queue behind it folded) → **Gated** → **Recent rulings**.

No `## Where to look` bullets ride on the page — they stay in the phase file,
check-in** stamp under it. Then **Summary** (one row per active project —
its next task by that task's own summary), then **Projects** (active →
planned → dormant; each card is the folders, the counts and **every open
task of the project, one line each**: the ten-word `Summary:` as the link, a
state pill coloured by `STATE_TONES` — green planned/ready/accepted, yellow
submitted/running/pulled/awaiting-ruling, grey gated, **red** whenever a run
failed, timed out or was voided — and only the facets that state earns) →
**Awaiting ruling** (ordered failures → a ruling is required → clean) →
**Recent rulings**.

The `Running / submitted`, `Ready` and `Gated` sections were dropped on
2026-09-07, with the "N more open phase(s)" fold: they said again, in three
more places, what the Projects block now says once, per project, in colour.
The census still cuts those buckets — they are `collect`'s and the door's
scopes — they are simply not sections of the page. Paths render as accent
chips (`.pathchip`, `--accent` fill with the theme's `--accent-ink`), not as
pink-on-tint inline code.

No `## Where to look` bullets ride on the page — they stay in the task file,
in `census --by-project` and in the check-in printout, which keep the full
per-phase tree. The Cortex has no epics of its own (retired 2026-09-04): a
science project *is* the long programme, and `Epic:` on a phase header is
only an optional join key to a **Mind** epic.
per-task tree. The Cortex has no epics of its own (retired 2026-09-04): a
science project *is* the long programme, and `Epic:` on a task header is
only an optional join key to a **Mind** epic (Mind epics really are
sequential; a Cortex task is not — that is why the number went).

**`checkin.yaml`** is the one-key file (`refreshed: <UTC ISO 8601>`) that
`cmd_checkin` and `collect --apply` write from the stamp they scored against,
Expand All @@ -94,49 +106,49 @@ rule of its own: it is stable between renders.
## Gates

`gates` is a thin wrapper over the Cortex script's own `gates_report(root)`:
every gated phase, its refs and the URL each ref resolves to. Nothing polls
every gated task, its refs and the URL each ref resolves to. Nothing polls
GitHub and nothing flips a state — gate grading was retired on 2026-09-03 (2
gated refs, 0 flips in its lifetime; sequencing is prose `Ready when:` lines
per Cortex schema decision 54). A human opens the refs and, when they have
closed, types `python3 scripts/cortex.py move <phase> ready`.
closed, types `python3 scripts/cortex.py move <task> ready`.

## The door (`checkin`)

The verb a human types. It composes what is already here and reasons nothing
extra of its own:

1. **Sync.** Every project with `status: active` in `projects.yaml`, plus any
project that owns a phase in `submitted | running` (a dormant project with a
project that owns a task in `submitted | running` (a dormant project with a
job still out there is still out there). Each is pulled with **its own**
`<local_path>/<sync_cli> pull` — the verb all seven implement — and the
output is **streamed**, not captured: a pull runs for minutes and the human
is watching this one command. A non-zero exit is recorded against that
project and the sweep continues; one unreachable mirror must not hide the
other six. After a good pull the door writes `<pull root>/.cortex/pull.json`
(`project`, `pulled_at`, `cmd`, `rc`, `phases_live`) — **merged** into
(`project`, `pulled_at`, `cmd`, `rc`, `tasks_live`) — **merged** into
whatever is already there, because one project's own CLI writes the richer
`checkpoints` / `runs` tables the scorer's checkpoint leg reads.
2. **Score + move.** `collect`'s scorer over every live phase, then the same
2. **Score + move.** `collect`'s scorer over every live task, then the same
rehearsed `_apply_checked` moves.
3. **Render.** `dashboard.md` + `dashboard.html`.
4. **Push** (the rule below).
5. **Summarise, by project** — printed **last**, so a chat sees it above the
fold: each project's paths, what its pull did, its phase counts, and every
phase a human could act on today with its health verdict and the fenced,
fold: each project's paths, what its pull did, its task counts, and every
task a human could act on today with its health verdict and the fenced,
copy-ready prompt its state already has (`_ruling_payload`,
`_live_payload`, `_ready_payload`, `_gate_payload`). `project_digest()`
builds one project's block and `checkin_summary()` the whole thing.

`--dry-run` (the default) prints the exact pull command per project, the pull
root, and every phase it would score — and reaches nothing at all. `--project
root, and every task it would score — and reaches nothing at all. `--project
KEY` narrows the sweep; `--skip-pull` re-scores what is already on the laptop,
stamping the refresh from the newest manifest.

### The push rule

`--push` is allowed only when **`gh auth status` succeeds** *and* **the Cortex
checkout is clean on `main`** — read *before* anything is written, because
"clean" stops being true the moment the phases move. That is the whole
"clean" stops being true the moment the tasks move. That is the whole
cloud/laptop split, and it is also the default: a laptop pushes without asking,
a cloud session cannot and says so. The push cuts
`claude/checkin-<YYYY-MM-DD>` from a fresh `origin/main`, commits the changed
Expand All @@ -148,9 +160,9 @@ same-day re-check-in reuses its branch rather than resetting a pushed ref.
## The check-in (`collect`)

This is the verb the human actually drives: *what came back?* With no
`--phase` it scopes to **every phase in `submitted | running`** — the runs the
Cortex believes are out there — and needs no record of any kind. `--phase REL`
(repeatable) narrows it to named phases. Each is scored against **six legs**,
`--task` it scopes to **every task in `submitted | running`** — the runs the
Cortex believes are out there — and needs no record of any kind. `--task REL`
(repeatable) narrows it to named tasks. Each is scored against **six legs**,
each `PASS | FAIL | UNOBSERVABLE`:

| Leg | Read from |
Expand All @@ -160,7 +172,7 @@ each `PASS | FAIL | UNOBSERVABLE`:
| `version` | a top-level `version` key in a JSON matching the project's `witness_file` |
| `checkpoint` | `<root>/.cortex/pull.json` only (`{"schema": 1, "pulled_at": ISO, "checkpoints": {"<run dir rel to the pull root>": {"bytes": N, "mtime": ISO}}, "runs": {"<jobid>": {"checkpoint_bytes": N, "checkpoint_mtime": ISO}}}`; looked up `runs[<jobid>]` → `runs[<stem>]` → `checkpoints[<run dir rel>]`, and a manifest with no `schema` is the older `runs`-only shape) — `search_internal/checkpoint.hdf5` is never pulled, so without that manifest the leg is UNOBSERVABLE, tagged `RAL only` |
| `resume` | `Fit Already Completed` / `Resuming … previous samples found` in the `.out` ⇒ FAIL: those are the previous fit's numbers |
| `witness` | a file matching `witness_file` newer than the phase's first submission |
| `witness` | a file matching `witness_file` newer than the task's first submission |

HEALTH is **FAILED** if any leg failed, **SUSPECT** if any is unobservable,
**HEALTHY** otherwise; `delivered:` counts the HEALTHY ones. Two of the four
Expand All @@ -174,14 +186,14 @@ search roots are the project's `mirror` then `local_path`, from
`projects.yaml`. **The only thing that reaches the cluster is `--pull`**, which
runs that project's own `<sync_cli> pull` and prints the command first.
`--apply` (which needs a refresh stamp, from `--pull` or `--refreshed <ISO>`,
so a human cannot move phases on a stale laptop) moves each phase
so a human cannot move tasks on a stale laptop) moves each task
`running → pulled → awaiting-ruling` — rehearsed on a throwaway copy of the
tree first and refused outright if `cortex.py check` would not pass afterwards.
Nothing else is written. A phase whose run line is still live, or one still
Nothing else is written. A task whose run line is still live, or one still
`submitted` (no such edge), is left where it is with a note.

## What it never does

Submits a job · writes or edits a ruling · reads `sacct` as health · touches
RAL · consults an autonomy cap · edits a phase except through the Cortex
RAL · consults an autonomy cap · edits a task except through the Cortex
script's own `move`.
Loading
Loading