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
8 changes: 5 additions & 3 deletions .github/workflows/cortex_check.yml
Original file line number Diff line number Diff line change
Expand Up @@ -45,9 +45,11 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: dependencies
# `cortex.py` reads projects.yaml with PyYAML, so the deps go in before
# the check step, not only before the tests.
run: python3 -m pip install --quiet pytest pyyaml
- name: cortex check
run: python3 scripts/cortex.py check
- name: tests
run: |
python3 -m pip install --quiet pytest pyyaml
python3 -m pytest tests -q
run: python3 -m pytest tests -q
11 changes: 7 additions & 4 deletions .github/workflows/dashboard_refresh.yml
Original file line number Diff line number Diff line change
Expand Up @@ -57,8 +57,8 @@ permissions:
actions: write

# Every workflow in this repo that can push to main shares one queue —
# ledger_merge.yml and gates_grade.yml carry the same group — so two bot writers
# never race for the tip and spend their retries re-merging. Queue rather than
# ledger_merge.yml carries the same group — so two bot writers never race for
# the tip and spend their retries re-merging. Queue rather than
# cancel: a cancelled heal can leave a rendered page uncommitted. PR runs never
# push, but they share the group rather than carry a second, ref-scoped one; the
# queue is short and one name is easier to reason about than two.
Expand All @@ -79,8 +79,11 @@ jobs:
with:
repository: PyAutoLabs/PyAutoBrain
path: PyAutoBrain
# No setup-python: the conductor and cortex.py are stdlib only, and the
# runner's python3 is the interpreter the laptop render is compared to.
# No setup-python: the runner's python3 is the interpreter the laptop
# render is compared to. PyYAML is `cortex.py`'s one dependency (it reads
# `projects.yaml`), and the conductor imports that script at runtime.
- name: dependencies
run: python3 -m pip install --quiet pyyaml
- name: dashboard freshness (self-healing on push to main)
working-directory: PyAutoCortex
run: |
Expand Down
114 changes: 0 additions & 114 deletions .github/workflows/gates_grade.yml

This file was deleted.

11 changes: 8 additions & 3 deletions .github/workflows/ledger_merge.yml
Original file line number Diff line number Diff line change
Expand Up @@ -68,9 +68,9 @@ permissions:
# leave a branch merged and undeleted.
#
# The group is shared by EVERY workflow here that can push to main —
# dashboard_refresh.yml's self-heal and gates_grade.yml's flips carry the same
# name — because the race is not between two merges, it is between two writers
# of the tip. One queue, and each writer sees a settled main.
# dashboard_refresh.yml's self-heal carries the same name — because the race is
# not between two merges, it is between two writers of the tip. One queue, and
# each writer sees a settled main.
concurrency:
group: cortex-main-writers
cancel-in-progress: false
Expand Down Expand Up @@ -157,6 +157,11 @@ jobs:
# header contradicts, a dangling Supersedes:, a run line that does not
# parse — nothing heals that automatically, and merging it would put the
# contradiction on main. Also the only check audit mode gets to report.
# `cortex.py` reads `projects.yaml` with PyYAML — its one dependency.
- name: dependencies
if: steps.classify.outputs.verdict == 'ledger'
run: python3 -m pip install --quiet pyyaml

- name: Structural drift on the branch (blocking)
if: steps.classify.outputs.verdict == 'ledger'
run: python3 scripts/cortex.py check
Expand Down
4 changes: 0 additions & 4 deletions .github/workflows/pages_dashboard.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@ on:
branches: [main]
paths:
- "dashboard.html"
- "batches/packets/**"
workflow_dispatch:

permissions:
Expand Down Expand Up @@ -52,9 +51,6 @@ jobs:
run: |
mkdir -p _site
cp dashboard.html _site/index.html
# Batch review packets — served under /packets/, as the Mind's are.
mkdir -p _site/packets
find batches/packets -maxdepth 1 -name "*.html" -exec cp {} _site/packets/ \;
- uses: actions/upload-pages-artifact@v3
with:
path: _site
Expand Down
65 changes: 29 additions & 36 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -65,25 +65,26 @@ human's turn. Full rules in [rulings/AGENTS.md](rulings/AGENTS.md).

- **`projects.yaml`** — the science body map. One row per project. **This is
code, not ledger**: `sync_cli` and `local_path` are paths a conductor will
execute under, so a change to it is always a human's turn. It is written in a
restricted YAML subset that `cortex.py` parses with the stdlib (REFERENCE.md
"The restricted YAML subset").
execute under, so a change to it is always a human's turn. `cortex.py` reads
it with PyYAML and validates the fields (REFERENCE.md "projects.yaml").
- **`phases/<project>/<slug>.md`** — one file per phase, with the Mind's light
header (`Key: value` lines, no YAML frontmatter) and a `## Runs` body in the
SLURM run-line grammar. `State:` is one of `planned | gated | ready |
submitted | running | pulled | awaiting-ruling | accepted | rerun | dropped`.
`legacy` and `legacy_wrong` are states of a **run**, never of a phase.
- **`rulings/<YYYY>/<MM>/R-<YYYYMMDD>-<nn>.md`** — the ledger of record.
- **`batches/`** — the rolling review board: one record per slot, the archived
packet page under `packets/`, the human's verbatim review under `reviews/`.
- **`batches/`** — closed history. Three 2026-08/09 batch records and the
human's verbatim reviews under `reviews/`, kept because 13 rulings cite them
and append-only for the merge gate: never modified, only added. Nothing
writes new ones (the review-slot apparatus was retired 2026-09-03).
- **`epics.md`** — the Cortex half of every split epic; `- mind-half:` names
the Mind entry by slug.
- **`scripts/cortex.py`** — the one lifecycle script (stdlib only):
- `check` — every structural rule, hermetic; `cortex check: OK` or `DRIFT`
with one ` - …` line per finding, exit 1.
- `gates [--grade [--write]]` — offline: the gated phases and their refs;
`--grade` polls GitHub and reports which gates cleared; `--write` flips
`gated → ready` (and `ready → gated` on a reopened gate).
- `gates` — read-only and offline: every gated phase, its refs and their
URLs. Nothing polls GitHub and nothing flips a state; a human reads the
listing, opens the refs and types `move <phase> ready`.
- `rule <phase> <verb> --body <file> …` — assigns the next ruling id, writes
the ruling file(s), updates the phase's `Ruling:` and `State:`. The only
door to `accepted`, `rerun`, `dropped` and `leave-to-finish`.
Expand Down Expand Up @@ -116,50 +117,41 @@ state table.
|------|--------------|
| `census [--json]` | what the Cortex is holding, by state — the one-screen answer |
| `dashboard --check` \| `--apply` | render `dashboard.md` + `dashboard.html`; `--check` exits **1 on drift**, **2 on bad args**, anything else = the renderer could not run |
| `gates [--grade] [--apply]` | the gate refs; `--grade` polls GitHub, `--apply` flips `gated → ready` (and `ready → gated` on a reopen) |
| `plan [--budget N]` | which `ready` phases fit a laptop slot, cheapest first; it hands over a command, never a decision |
| `collect [--slot S] [--pull] [--refreshed ISO] [--apply]` | score a pulled run's legs into a packet member; `--pull` is opt-in and runs the *project's own* sync CLI |

The batch conductor is the **slot door** over the same two verbs:
`pyauto-brain batch plan --kind cortex` and `batch collect --kind cortex` drive
this `plan` and `collect` for one review slot and write `batches/` — the record,
its `refreshed:` lines and the packet (`batches/AGENTS.md`).
| `gates` | every gated phase, its refs and their URLs — read-only, offline |
| `collect [--phase REL] [--pull] [--refreshed ISO] [--apply]` | score what came back; with no `--phase` it scopes to **every** `submitted \| running` phase, and `--pull` runs the *project's own* sync CLI |

**`--apply` here, `--write` there.** The conductor's verbs spell the writing
flag `--apply` (the Brain's house spelling, as intake does); `scripts/cortex.py`
spells it `--write`. `cortex gates --grade --apply` is a thin wrapper over
`cortex.py gates --grade --write` — same edit, two doors.
spells it `--write`.

**Nothing here submits a job.** `plan` prints the project's own `sync_cli
submit` line and the `cortex.py move <phase> submitted --run <jobid>` follow-up;
a human runs both. `collect --pull` is the one leg that shells out, and only to
the project's own CLI.
**Nothing here submits a job.** A human runs the project's own `sync_cli
submit` line and the `cortex.py move <phase> submitted --run <jobid>` follow-up.
`collect --pull` is the one leg that shells out, and only to the project's own
CLI.

### What runs by itself

Four workflows, and only these may write:
Three workflows, and only these may write:

| Workflow | Trigger | May write |
|---|---|---|
| `cortex_check.yml` | push/PR on ledger, scripts, tests, the dashboards | **nothing** — `cortex.py check` + pytest |
| `dashboard_refresh.yml` | push to main, PR, nightly 03:35 UTC, dispatch | `dashboard.md`, `dashboard.html` (self-heal on main; a PR run errors instead of healing) |
| `pages_dashboard.yml` | push to `dashboard.html` / `batches/packets/**`, dispatch | nothing in the repo — it publishes to Pages |
| `gates_grade.yml` | daily 06:47 UTC, dispatch | phase headers — **`State:` and `Gates-cleared:` only**, and only `gated → ready` (or `ready → gated` on a reopened gate) |
| `pages_dashboard.yml` | push to `dashboard.html`, dispatch | nothing in the repo — it publishes to Pages |
| `ledger_merge.yml` | push to `claude/**`, dispatch | merges a ledger-only branch to main |

`gates_grade.yml` is **the one scheduled job that mutates the ledger**. It never
rules, never submits, never edits a run line. An unreadable gate ref fails
closed: the phase is skipped, whatever else flipped is still committed, and the
job then goes red so a human sees the ref.
**No scheduled job mutates the ledger.** A daily gate-grading cron did, until
gate grading was retired on 2026-09-03 (2 gated refs, 0 flips in its
lifetime); the only self-healing writer left renders the two generated pages.

All three main-writers share `concurrency: group: cortex-main-writers`, so two
The main-writers share `concurrency: group: cortex-main-writers`, so two
bots never race for the tip of main. And because a `GITHUB_TOKEN` push triggers
no workflow at all, each of them re-dispatches by name what its push should have
woken (`pages_dashboard.yml` after a heal; `cortex_check.yml` +
`dashboard_refresh.yml` after a grading commit).

The board is published at **<https://pyautolabs.github.io/PyAutoCortex/>** —
`dashboard.html` as the index, archived packets under `/packets/`.
`dashboard.html` as the index.

## The workspace-paths exception

Expand All @@ -172,7 +164,7 @@ exception is stated in the file's header and here, and nowhere else — a phase
file or a ruling points into a project through its `projects.yaml` row, never
with a bare absolute path of its own.

## The laptop lane — what is out of scope, and why
## What is out of scope, and why

Quoted verbatim from `PyAutoMind/complete/archive/shelved/batch_science_lane.md`
so nobody re-derives these:
Expand All @@ -195,8 +187,9 @@ so nobody re-derives these:
> prompts that they are human-driven and supervised with a judged verdict as the
> deliverable — **no transport was ever going to make those unattended.**

Every Cortex phase is therefore `Lane: local-dev`, always; the field exists so
the batch conductor's lane filter reads one vocabulary across both surfaces.
Every Cortex phase therefore runs on the laptop. That was once written into
each phase file as `Lane: local-dev` — 32 of 32 said the same thing, so the
header was deleted on 2026-09-03 and the fact lives here instead.

## Hard rules

Expand All @@ -208,8 +201,8 @@ the batch conductor's lane filter reads one vocabulary across both surfaces.
or batch change; `ledger_merge.yml` runs it on the trial-merge tree and a
failing check leaves the branch for a human.
5. **No hand-written HTML.** `dashboard.html` is rendered by the cortex
conductor and packets by the batch conductor; a hand edit is drift that
`dashboard_refresh.yml` will overwrite on the next push to `main`.
conductor; a hand edit is drift that `dashboard_refresh.yml` will overwrite
on the next push to `main`.

<!-- repos_sync:remote:begin -->
## Remote sessions (Claude Code on web and mobile)
Expand Down
22 changes: 14 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,8 @@ and ends in a **ruling**:
it `submitted` and then `running`, with every job id recorded in the file's
`## Runs` block in SLURM notation.
3. **Pull and review.** When the results are pulled to the laptop the phase
joins the **rolling review board** (`batches/`); a member joins when its
results are in hand, never mid-flight.
becomes `pulled` and then `awaiting-ruling` — it is waiting on the human's
eyes, and on nothing else.
4. **Rule.** The human's verdict — `accept`, `rerun`, `drop` or
`leave-to-finish` — is written into `rulings/` with a permanent id. *A
verdict recorded only outside the Cortex does not exist.*
Expand All @@ -34,18 +34,24 @@ In full those four steps are ten states — `planned`, `gated`, `ready`,
`submitted`, `running`, `pulled`, `awaiting-ruling`, then `accepted`, `rerun`
or `dropped` — of which only the last three are reachable, and only through
`scripts/cortex.py rule` (`legacy` and `legacy_wrong` are states of a *run*,
never of a phase). The review slot itself is opened and refreshed from the
Brain's batch conductor: `pyauto-brain batch plan --kind cortex --apply
--review-at <ISO>` writes the record under `batches/`, and `batch collect
--kind cortex` fills the board in as results are pulled.
never of a phase). Checking in is one command from the Brain's cortex
conductor — `pyauto-brain cortex collect --pull --apply` pulls every submitted
or running phase's results, scores them and moves what came back to
`awaiting-ruling`. `batches/` is closed history: the three 2026-08/09 batch
records and the human's verbatim reviews stay readable because 13 rulings cite
them, and nothing writes new ones.

The Cortex is **not a second PyAutoMind**: development tasks stay in the Mind
and are named here only as gates. Its one script, `scripts/cortex.py`, checks
the tree and moves phases between states; the ledger folders auto-merge from
any surface that can push.

The schemas — phase files, the run-line grammar, rulings, batches, the
`projects.yaml` subset — are in [REFERENCE.md](REFERENCE.md); how agents should
`scripts/cortex.py` needs Python 3.10+ and **PyYAML** (`python3 -m pip install
pyyaml`), which is how it reads `projects.yaml`; the tests additionally need
`pytest`. Nothing else.

The schemas — phase files, the run-line grammar, rulings, the `projects.yaml`
fields — are in [REFERENCE.md](REFERENCE.md); how agents should
operate this repo is in [AGENTS.md](AGENTS.md); every design choice the birth
epic did not fix is dated in [docs/schema_decisions.md](docs/schema_decisions.md).
The organism this repo is the Cortex of is described once in
Expand Down
Loading
Loading