From fa82e4a569662c357dc58779d705b4270efb66de Mon Sep 17 00:00:00 2001 From: Jammy2211 Date: Mon, 7 Sep 2026 14:12:56 -0400 Subject: [PATCH 1/3] =?UTF-8?q?cortex:=20tasks,=20not=20phases=20=E2=80=94?= =?UTF-8?q?=20slug=20is=20the=20identity,=20ten-word=20Summary?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Science is unordered ideas, not a numbered sequence: phases/ → tasks/, the Phase: number is deleted, Summary: (≤10 words, the question the task answers) is required and checked, rulings point at Task:. Folds in the wording of #17. Refs #20. Co-Authored-By: Claude Fable 5.1 Claude-Session: https://claude.ai/code/session_01EpM4gQikgEtCqpBJ3hzgct --- .github/workflows/cortex_check.yml | 8 +- .github/workflows/dashboard_refresh.yml | 8 +- .github/workflows/ledger_merge.yml | 10 +- .github/workflows/pages_dashboard.yml | 4 +- AGENTS.md | 80 +-- README.md | 20 +- REFERENCE.md | 208 ++++---- batches/reviews/AGENTS.md | 2 +- docs/schema_decisions.md | 25 + projects.yaml | 6 +- rulings/2026/08/R-20260831-01.md | 4 +- rulings/2026/08/R-20260831-02.md | 4 +- rulings/2026/08/R-20260831-03.md | 4 +- rulings/2026/08/R-20260831-04.md | 4 +- rulings/2026/08/R-20260831-05.md | 4 +- rulings/2026/08/R-20260831-06.md | 4 +- rulings/2026/08/R-20260831-07.md | 4 +- rulings/2026/08/R-20260831-08.md | 4 +- rulings/2026/09/R-20260901-01.md | 4 +- rulings/2026/09/R-20260901-02.md | 4 +- rulings/2026/09/R-20260901-03.md | 4 +- rulings/2026/09/R-20260901-04.md | 4 +- rulings/2026/09/R-20260902-01.md | 4 +- rulings/2026/09/R-20260902-02.md | 4 +- rulings/2026/09/R-20260902-03.md | 4 +- rulings/2026/09/R-20260902-04.md | 4 +- rulings/2026/09/R-20260902-05.md | 4 +- rulings/2026/09/R-20260902-06.md | 4 +- rulings/2026/09/R-20260902-07.md | 4 +- rulings/2026/09/R-20260902-08.md | 4 +- rulings/2026/09/R-20260902-09.md | 4 +- rulings/2026/09/R-20260902-10.md | 4 +- rulings/2026/09/R-20260904-01.md | 4 +- rulings/2026/09/R-20260904-02.md | 4 +- rulings/2026/09/R-20260904-03.md | 4 +- rulings/2026/09/R-20260904-04.md | 4 +- rulings/2026/09/R-20260904-05.md | 4 +- rulings/2026/09/R-20260907-01.md | 4 +- rulings/AGENTS.md | 22 +- scripts/cortex.py | 497 +++++++++--------- scripts/ledger_merge.py | 10 +- .../euclid/magnification_robustness.md | 15 +- .../resimulate_fitted_lens_simulator.md | 18 +- .../euclid/sersic_index_recovery.md | 17 +- .../dr1_prelim_10_lens_science_run.md | 9 +- .../ic50_workspace/ep_scale_up.md | 4 +- .../cluster_extended_source_inference.md | 4 +- .../cluster_gradient_search_benchmark.md | 4 +- .../delaunay_fp64_retro_baseline.md | 8 +- .../failed_submissions_342008_10.md | 4 +- .../inference_refs_v1_redo.md | 6 +- .../inference_programme/knn_rerun_342016_7.md | 4 +- .../legacy_point_output_sweep.md | 8 +- .../likelihood_term_levers.md | 10 +- .../mass_pix_gradient_cost_probe.md | 8 +- .../mge_fp64_retro_baseline.md | 4 +- .../inference_programme/mge_pos_ref_reuse.md | 4 +- .../multiband_compile_census_completion.md | 4 +- .../nautilus_mass_pix_baseline.md | 14 +- .../phase2_nss_mainline_gate_a_reuse.md | 4 +- ...hase3_prodigy_reliability_gate_b1_reuse.md | 4 +- .../phase4_positions_gate_b2_reuse.md | 4 +- .../phase5_mesh_gradient_positions_on.md | 16 +- .../inference_programme/prodigy_mass_pix.md | 14 +- .../inference_programme/refs_5_6_342016_56.md | 4 +- .../refs_v1_positions_on_completion.md | 6 +- .../inference_programme/rewind_2026_08_31.md | 4 +- .../inference_programme/slogdet_ab_342017.md | 4 +- .../inference_programme/smc_probe_342018.md | 4 +- .../slope_hierarchy/methods_writeup.md | 6 +- .../slope_hierarchy/n25_scale_up.md | 4 +- .../delaunay_adapt_split_pl_eff_0.md | 4 +- .../delaunay_adapt_split_pl_eff_1_outer.md | 4 +- .../delaunay_adapt_split_pl_sersic_0.md | 4 +- .../rectangular_adapt_pl_eff_0.md | 4 +- .../rectangular_adapt_pl_eff_1_outer.md | 4 +- .../rectangular_adapt_pl_sersic_0.md | 6 +- .../fixtures/empty/{phases => tasks}/.gitkeep | 0 .../skeleton/batches/2026-09-01-pm.md | 4 +- .../skeleton/rulings/2026/09/R-20260901-01.md | 8 +- .../skeleton/rulings/2026/09/R-20260901-02.md | 12 +- .../skeleton/rulings/2026/09/R-20260901-03.md | 6 +- .../skeleton/rulings/2026/09/R-20260901-04.md | 6 +- .../skeleton/rulings/2026/09/R-20260901-05.md | 6 +- .../{phases => tasks}/example/01_scope.md | 6 +- .../example/02_gated_on_dev.md | 8 +- .../example/03_ready_cleared.md | 8 +- .../example/04_submitted_override.md | 8 +- .../example/05_running_array.md | 10 +- .../{phases => tasks}/example/06_pulled.md | 12 +- .../example/07_awaiting_ruling.md | 12 +- .../{phases => tasks}/example/08_accepted.md | 10 +- .../{phases => tasks}/example/09_rerun.md | 10 +- .../{phases => tasks}/example/10_dropped.md | 10 +- tests/test_cortex.py | 248 +++++---- tests/test_ledger_merge.py | 52 +- 96 files changed, 909 insertions(+), 784 deletions(-) rename {phases => tasks}/euclid/magnification_robustness.md (83%) rename {phases => tasks}/euclid/resimulate_fitted_lens_simulator.md (74%) rename {phases => tasks}/euclid/sersic_index_recovery.md (74%) rename {phases => tasks}/euclid_dr1_prelim/dr1_prelim_10_lens_science_run.md (95%) rename {phases => tasks}/ic50_workspace/ep_scale_up.md (94%) rename {phases => tasks}/inference_programme/cluster_extended_source_inference.md (91%) rename {phases => tasks}/inference_programme/cluster_gradient_search_benchmark.md (94%) rename {phases => tasks}/inference_programme/delaunay_fp64_retro_baseline.md (85%) rename {phases => tasks}/inference_programme/failed_submissions_342008_10.md (93%) rename {phases => tasks}/inference_programme/inference_refs_v1_redo.md (94%) rename {phases => tasks}/inference_programme/knn_rerun_342016_7.md (91%) rename {phases => tasks}/inference_programme/legacy_point_output_sweep.md (94%) rename {phases => tasks}/inference_programme/likelihood_term_levers.md (87%) rename {phases => tasks}/inference_programme/mass_pix_gradient_cost_probe.md (92%) rename {phases => tasks}/inference_programme/mge_fp64_retro_baseline.md (93%) rename {phases => tasks}/inference_programme/mge_pos_ref_reuse.md (93%) rename {phases => tasks}/inference_programme/multiband_compile_census_completion.md (93%) rename {phases => tasks}/inference_programme/nautilus_mass_pix_baseline.md (89%) rename {phases => tasks}/inference_programme/phase2_nss_mainline_gate_a_reuse.md (98%) rename {phases => tasks}/inference_programme/phase3_prodigy_reliability_gate_b1_reuse.md (98%) rename {phases => tasks}/inference_programme/phase4_positions_gate_b2_reuse.md (99%) rename {phases => tasks}/inference_programme/phase5_mesh_gradient_positions_on.md (95%) rename {phases => tasks}/inference_programme/prodigy_mass_pix.md (87%) rename {phases => tasks}/inference_programme/refs_5_6_342016_56.md (92%) rename {phases => tasks}/inference_programme/refs_v1_positions_on_completion.md (90%) rename {phases => tasks}/inference_programme/rewind_2026_08_31.md (95%) rename {phases => tasks}/inference_programme/slogdet_ab_342017.md (91%) rename {phases => tasks}/inference_programme/smc_probe_342018.md (93%) rename {phases => tasks}/slope_hierarchy/methods_writeup.md (95%) rename {phases => tasks}/slope_hierarchy/n25_scale_up.md (96%) rename {phases => tasks}/subhalo_validation/delaunay_adapt_split_pl_eff_0.md (94%) rename {phases => tasks}/subhalo_validation/delaunay_adapt_split_pl_eff_1_outer.md (95%) rename {phases => tasks}/subhalo_validation/delaunay_adapt_split_pl_sersic_0.md (95%) rename {phases => tasks}/subhalo_validation/rectangular_adapt_pl_eff_0.md (89%) rename {phases => tasks}/subhalo_validation/rectangular_adapt_pl_eff_1_outer.md (89%) rename {phases => tasks}/subhalo_validation/rectangular_adapt_pl_sersic_0.md (94%) rename tests/fixtures/empty/{phases => tasks}/.gitkeep (100%) rename tests/fixtures/skeleton/{phases => tasks}/example/01_scope.md (66%) rename tests/fixtures/skeleton/{phases => tasks}/example/02_gated_on_dev.md (62%) rename tests/fixtures/skeleton/{phases => tasks}/example/03_ready_cleared.md (59%) rename tests/fixtures/skeleton/{phases => tasks}/example/04_submitted_override.md (67%) rename tests/fixtures/skeleton/{phases => tasks}/example/05_running_array.md (70%) rename tests/fixtures/skeleton/{phases => tasks}/example/06_pulled.md (53%) rename tests/fixtures/skeleton/{phases => tasks}/example/07_awaiting_ruling.md (62%) rename tests/fixtures/skeleton/{phases => tasks}/example/08_accepted.md (63%) rename tests/fixtures/skeleton/{phases => tasks}/example/09_rerun.md (64%) rename tests/fixtures/skeleton/{phases => tasks}/example/10_dropped.md (70%) diff --git a/.github/workflows/cortex_check.yml b/.github/workflows/cortex_check.yml index 44cd29c..3d49597 100644 --- a/.github/workflows/cortex_check.yml +++ b/.github/workflows/cortex_check.yml @@ -1,8 +1,8 @@ name: Cortex Check -# Guards the run-and-ruling registry: every phase file parses and its state is +# Guards the run-and-ruling registry: every task file parses and its state is # legal, every ruling id is unique and every Supersedes: resolves, every -# project a phase names is in projects.yaml, the gates/witness invariants hold +# project a task names is in projects.yaml, the gates/witness invariants hold # (`scripts/cortex.py check`), then the script's own tests. No self-heal here: # a failing check is a real contradiction and only a human can resolve it. The # generated pages that DO self-heal (`dashboard.md`, `dashboard.html`) are @@ -13,7 +13,7 @@ on: push: branches: [main] paths: - - "phases/**" + - "tasks/**" - "rulings/**" - "batches/**" - "projects.yaml" @@ -25,7 +25,7 @@ on: - ".github/workflows/cortex_check.yml" pull_request: paths: - - "phases/**" + - "tasks/**" - "rulings/**" - "batches/**" - "projects.yaml" diff --git a/.github/workflows/dashboard_refresh.yml b/.github/workflows/dashboard_refresh.yml index 6e65cd5..340278a 100644 --- a/.github/workflows/dashboard_refresh.yml +++ b/.github/workflows/dashboard_refresh.yml @@ -2,9 +2,9 @@ name: Dashboard Refresh # Keeps the generated Cortex pages (`dashboard.md`, linked from the README, and # `dashboard.html`, the Pages index) in step with the registry they describe. -# The pages are rendered by PyAutoBrain's cortex conductor from `phases/`, +# The pages are rendered by PyAutoBrain's cortex conductor from `tasks/`, # `rulings/`, `batches/`, `projects.yaml` and `checkin.yaml`, so any push that -# files a phase, moves it, records a run, lands a ruling or stamps a check-in +# files a task, moves it, records a run, lands a ruling or stamps a check-in # makes them stale. # # Same shape as the Mind's dashboard_refresh.yml, and for the same reason: on @@ -30,7 +30,7 @@ on: push: branches: [main] paths: - - "phases/**" + - "tasks/**" - "rulings/**" - "batches/**" - "projects.yaml" @@ -39,7 +39,7 @@ on: - "dashboard.html" pull_request: paths: - - "phases/**" + - "tasks/**" - "rulings/**" - "batches/**" - "projects.yaml" diff --git a/.github/workflows/ledger_merge.yml b/.github/workflows/ledger_merge.yml index 8061320..0a50911 100644 --- a/.github/workflows/ledger_merge.yml +++ b/.github/workflows/ledger_merge.yml @@ -3,7 +3,7 @@ name: Cortex Ledger Merge # Lands PyAutoCortex's own ledger work without anyone having to ask for it. # # THE PROBLEM. A branch-scoped session — the phone, claude.ai/code, any -# `claude/**` flow — pushes a phase move, a ruling, a batch record to a feature +# `claude/**` flow — pushes a task move, a ruling, a batch record to a feature # branch and it stops there: nothing downstream looks at a `claude/**` push. # The Mind learned this first (mind_ledger_merge.yml); the Cortex is born with # the answer. @@ -15,7 +15,7 @@ name: Cortex Ledger Merge # pushed it ever comes back. # # WHAT IT WILL AND WILL NOT MERGE. Only a branch whose whole diff is *ledger*: -# `phases/`, `rulings/`, `batches/`, `checkin.yaml`. The line is drawn by +# `tasks/`, `rulings/`, `batches/`, `checkin.yaml`. The line is drawn by # scripts/ledger_merge.py — a script, so it is testable and a session can # predict the verdict — and it is DEFAULT DENY: anything under `scripts/`, # `tests/`, `.github/`, `policy/`, `docs/`, `projects.yaml`, the prose pages, @@ -153,7 +153,7 @@ jobs: echo "verdict=ledger" >> "$GITHUB_OUTPUT" fi - # Structural drift on the branch tip BLOCKS. A phase in a state its + # Structural drift on the branch tip BLOCKS. A task in a state its # 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. @@ -206,11 +206,11 @@ jobs: fi # THE TRIAL-MERGE CHECK. The branch passed alone; does it pass # merged? This is where two branches that both assigned the same - # ruling id, or both moved the same phase, are caught. + # ruling id, or both moved the same task, are caught. if ! python3 scripts/cortex.py check; then git reset --hard origin/main echo "::error::'$BRANCH' merges cleanly but 'cortex.py check' fails on the merged tree — left for a human" - echo "**Blocked** — \`$BRANCH\` fails \`cortex.py check\` once merged with main (a ruling-id or phase-state race?); left for a human." >> "$GITHUB_STEP_SUMMARY" + echo "**Blocked** — \`$BRANCH\` fails \`cortex.py check\` once merged with main (a ruling-id or task-state race?); left for a human." >> "$GITHUB_STEP_SUMMARY" exit 1 fi if [ -n "$PR" ]; then diff --git a/.github/workflows/pages_dashboard.yml b/.github/workflows/pages_dashboard.yml index 45a5f24..6a05ef4 100644 --- a/.github/workflows/pages_dashboard.yml +++ b/.github/workflows/pages_dashboard.yml @@ -6,7 +6,7 @@ name: Pages Dashboard # Why a twin exists at all: GitHub's markdown rendering strips JavaScript, so # `dashboard.md` can never write to the clipboard — on the GitHub mobile app its # fenced blocks have no copy affordance at all, leaving long-press text -# selection as the only way to grab a phase's command. The HTML page carries a +# selection as the only way to grab a task's command. The HTML page carries a # real copy button instead: one tap and the command is on the clipboard, ready # to paste into a Claude Code chat. It is also where a review is done — the # board is read on a phone as often as on the laptop. @@ -15,7 +15,7 @@ name: Pages Dashboard # `dashboard.md` (one census, two pages) and kept fresh by the same self-heal in # `dashboard_refresh.yml`; this workflow only publishes it. # Only the uploaded files reach Pages — nothing else in the repo, and in -# particular not `phases/`, `rulings/` or `projects.yaml`, whose absolute laptop +# particular not `tasks/`, `rulings/` or `projects.yaml`, whose absolute laptop # paths are not for a public site. on: diff --git a/AGENTS.md b/AGENTS.md index 18402cb..3d14104 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -31,7 +31,7 @@ Generated from `PyAutoMind/repos.yaml` + `PyAutoBrain/ORGANISM.md`; edit there, **The Mind decides what to build, the Brain routes the work and executes nothing, the Cortex learns what is true.** The Cortex is a **run-and-ruling -registry**: `project → phase → runs → rulings`. It owns two pieces of state no +registry**: `project → task → runs → rulings`. It owns two pieces of state no other organ holds — the **science body map** (`projects.yaml`: every science project, where it lives on the laptop and on RAL, how it syncs, where its own commentary ledger is) and the **rulings ledger of record** (`rulings/`: every @@ -39,13 +39,21 @@ verdict a human has passed on a science run, append-only). It is **not a second PyAutoMind.** The Mind is PR-shaped all the way down — prompts, issues, branches, completion records, "delivered = a PR with a diff -and checks". The Cortex's unit is a **phase** of a **project**, which spawns -**runs** (SLURM job ids) and ends in a **ruling**. Development tasks a science -phase waits on stay in the Mind and are named here only as **gates** — GitHub -issue/PR refs in a phase's `Gates:` header, one grammar, one direction. The +and checks". The Cortex's unit is a **task** of a **project**, which spawns +**runs** (SLURM job ids) and ends in a **ruling**. Development work a science +task waits on stays in the Mind and is named here only as **gates** — GitHub +issue/PR refs in a task's `Gates:` header, one grammar, one direction. The Mind learns nothing about the Cortex beyond a render-time badge. -The schemas — phase files, the run-line grammar, rulings, batches, the +**Tasks, not phases** (2026-09-07). A project holds *unordered* tasks — ideas +the human runs in whatever order the results and the priorities dictate — so +there is no number and no sequence. A task's identity is its **slug**, unique +per project by the path itself, and every task carries a required `Summary:` +of at most ten words: the *question* it answers, which is the one line the +board shows. Mind **epics** are still numbered phases; that is development +work, and it really is sequential. + +The schemas — task files, the run-line grammar, rulings, batches, the restricted `projects.yaml` subset — and the `scripts/cortex.py` verbs are in [REFERENCE.md](REFERENCE.md). Every choice the birth epic did not fix is dated in [docs/schema_decisions.md](docs/schema_decisions.md); read decisions there, @@ -67,11 +75,13 @@ human's turn. Full rules in [rulings/AGENTS.md](rulings/AGENTS.md). 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. `cortex.py` reads it with PyYAML and validates the fields (REFERENCE.md "projects.yaml"). -- **`phases//.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 | +- **`tasks//.md`** — one file per task, with the Mind's light + header (`Key: value` lines, no YAML frontmatter — `Project:`, `Summary:`, + `State:`, …) and a `## Runs` body in the SLURM run-line grammar. There is no + `Phase:` number: the slug is the identity, and `check` names `Phase:` as a + retired key. `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. + `legacy` and `legacy_wrong` are states of a **run**, never of a task. - **`rulings///R--.md`** — the ledger of record. - **`batches/`** — closed history. Three 2026-08/09 batch records and the human's verbatim reviews under `reviews/`, kept because 13 rulings cite them @@ -85,27 +95,27 @@ human's turn. Full rules in [rulings/AGENTS.md](rulings/AGENTS.md). - **`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` — read-only and offline: every gated phase, its refs and their + - `gates` — read-only and offline: every gated task, its refs and their URLs. Nothing polls GitHub and nothing flips a state; a human reads the - listing, opens the refs and types `move ready`. - - `rule --body …` — assigns the next ruling id, writes - the ruling file(s), updates the phase's `Ruling:` and `State:`. The only + listing, opens the refs and types `move ready`. + - `rule --body …` — assigns the next ruling id, writes + the ruling file(s), updates the task's `Ruling:` and `State:`. The only door to `accepted`, `rerun`, `dropped` and `leave-to-finish`. - - `move …` — every other transition, per the table in + - `move …` — every other transition, per the table in REFERENCE.md; refuses the ruling edges. - - `new --phase …` — writes a phase file from the - template. + - `new --summary "<≤10 words>" …` — writes a task file + from the template; `--summary` is required and capped, as `check` is. - `retire --why ""` — the only verb that writes `projects.yaml`: the row's `status:` becomes `retired` and its - `note:` records `retired : `. The row, its phases and its - rulings all stay; it refuses while any phase of that project is + `note:` records `retired : `. The row, its tasks and its + rulings all stay; it refuses while any task of that project is outside `accepted | rerun | dropped | planned`. - **`dashboard.md` / `dashboard.html`** — GENERATED, never hand-edited: the board, rendered by the Brain's cortex conductor and self-healed on `main` (see "Driving the Cortex" below). They are ledger for the merge gate. - **`scripts/ledger_merge.py`** — the default-deny classifier behind `.github/workflows/ledger_merge.yml`: a `claude/**` push whose whole diff is - ledger (`phases/`, `rulings/`, `batches/`, `checkin.yaml`, the two generated + ledger (`tasks/`, `rulings/`, `batches/`, `checkin.yaml`, the two generated dashboards) lands on `main` by itself; anything else waits for a human — and that "anything else" includes an `AGENTS.md` or `TEMPLATE.md` *inside* a ledger dir, which is doctrine, not an entry. `python3 @@ -118,23 +128,26 @@ lives in the Brain's **cortex conductor** — `pyauto-brain cortex `, or `python3 PyAutoBrain/agents/conductors/cortex/_cortex.py --cortex ` with no Brain install. It is read-mostly: it renders, it plans and it scores. The only bytes it writes of its own are the two generated pages; -every change to a *phase* goes through `scripts/cortex.py`, which owns the +every change to a *task* goes through `scripts/cortex.py`, which owns the state table. | Verb | What it does | |------|--------------| -| `checkin [--dry-run \| --apply] [--push \| --no-push] [--project KEY] [--skip-pull]` | **the check-in** — the one door: pull every active project through its own `sync_cli`, score every live phase, move what came back, re-render the board, push the ledger where the rule allows, and summarise **by project**. `--dry-run` is the default and reaches nothing | +| `checkin [--dry-run \| --apply] [--push \| --no-push] [--project KEY] [--skip-pull]` | **the check-in** — the one door: pull every active project through its own `sync_cli`, score every live task, move what came back, re-render the board, push the ledger where the rule allows, and summarise **by project**. `--dry-run` is the default and reaches nothing | | `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` | 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 | +| `gates` | every gated task, its refs and their URLs — read-only, offline | +| `collect [--task REL] [--pull] [--refreshed ISO] [--apply]` | score what came back; with no `--task` it scopes to **every** `submitted \| running` task, 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`. -**Nothing here submits a job.** A human runs the project's own `sync_cli -submit` line and the `cortex.py move submitted --run ` follow-up. +**Nothing here submits a job by itself.** A submission is made only on the +human's ask — by the human, or by the agent in the session — with the project's +own `sync_cli submit` line, followed at once by `cortex.py move +submitted --run ` (ruled 2026-09-05; before that the human typed the +submit). `checkin` and `collect --pull` are the only legs that shell out, and only to the project's own CLI — the conductor adds no SSH of its own. Each project `checkin` pulls gets a `/.cortex/pull.json` manifest @@ -172,7 +185,7 @@ exception, and it is confined to `projects.yaml`: a project's `local_path` and `mirror` are absolute laptop paths **outside** the workspace (the Science folder under `/mnt/c/…`), because that is where the datasets, the `output/` trees and the pulled results live and the review happens at the laptop. The -exception is stated in the file's header and here, and nowhere else — a phase +exception is stated in the file's header and here, and nowhere else — a task file or a ruling points into a project through its `projects.yaml` row, never with a bare absolute path of its own. @@ -199,9 +212,12 @@ 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 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. +Every Cortex task therefore runs on the laptop. That was once written into +each task 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. (The quote +above is the Mind's, verbatim: the "phases 4, 6a and 6b" it names are phases of +the `euclid-dr1-prep` **Mind epic**, which is development work and genuinely +sequential — not Cortex tasks.) ## Hard rules @@ -209,8 +225,8 @@ header was deleted on 2026-09-03 and the fact lives here instead. 2. **Pull before edit.** `git fetch && git status` first, every time. 3. **Never edit a ruling.** Supersede it. `ledger_merge.py` refuses to auto-merge a modified or deleted file under `rulings/`. -4. **Run `python3 scripts/cortex.py check` before you push** a phase, ruling - or batch change; `ledger_merge.yml` runs it on the trial-merge tree and a +4. **Run `python3 scripts/cortex.py check` before you push** a task, ruling + or batch change (it is what refuses a missing or over-long `Summary:`); `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; a hand edit is drift that `dashboard_refresh.yml` will overwrite diff --git a/README.md b/README.md index 2a3497c..bb35e4b 100644 --- a/README.md +++ b/README.md @@ -7,23 +7,23 @@ body map** (every science project, where it lives, how it syncs) and the **rulings of record** — every verdict a human has passed on a science run, append-only, superseded but never edited. -**The board** — every phase the Cortex is holding, on one page, with the next +**The board** — every task the Cortex is holding, on one page, with the next command a tap away: (markdown twin: [dashboard.md](dashboard.md)). Both pages are generated by the Brain's cortex conductor and refreshed on every push; never hand-edit them. ## How PyAutoCortex works -The unit is a **phase** of a **project**, which spawns **runs** (SLURM job ids) +The unit is a **task** of a **project**, which spawns **runs** (SLURM job ids) and ends in a **ruling**: -1. **Declare the phase.** A markdown file under `phases//` states the +1. **Declare the task.** A markdown file under `tasks//` states the question, the pre-registered witness, the wall budget and the GitHub issues it waits on (`Gates:`). It starts `planned` or `gated`. -2. **Run it.** When the gates clear the phase is `ready`; a submission makes +2. **Run it.** When the gates clear the task is `ready`; a submission makes 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 +3. **Pull and review.** When the results are pulled to the laptop the task 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 @@ -34,25 +34,25 @@ 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). Checking in is one command from the Brain's cortex +never of a task). Checking in is one command from the Brain's cortex conductor — **`pyauto-brain cortex checkin --apply`** (see `--dry-run` first) pulls every active project through its own sync CLI, scores every submitted or -running phase against its pre-registered witness, moves what came back to +running task against its pre-registered witness, moves what came back to `awaiting-ruling`, re-renders the board, pushes the ledger where it is allowed -to, and prints a summary **by project** with the next prompt for each phase. `batches/` is closed history: the three 2026-08/09 batch +to, and prints a summary **by project** with the next prompt for each task. `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 +the tree and moves tasks between states; the ledger folders auto-merge from any surface that can push. `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` +The schemas — task 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). diff --git a/REFERENCE.md b/REFERENCE.md index 68dadc7..8448a8e 100644 --- a/REFERENCE.md +++ b/REFERENCE.md @@ -7,16 +7,16 @@ resolve here, one link from [AGENTS.md](AGENTS.md). --- -## What a phase looks like +## What a task looks like -Here is a phase file — `phases/example/05_running_array.md` from the test +Here is a task file — `tasks/example/05_running_array.md` from the test fixture — of a project whose GPU array is most of the way through: ````markdown -# Example — phase 5: the nine-lens array +# Example — the nine-lens array Project: example -Phase: 5 +Summary: Does Delaunay reach the same theta_E basin on ten lenses State: running Gates: PyAutoArray#431 Witness: nine of ten array tasks write a sane checkpoint.hdf5 within 8:00 wall @@ -33,16 +33,16 @@ Does the Delaunay pipeline reach the same theta_E basin on all ten lenses? ## Witness -`output/phase_05/*/checkpoint.hdf5` present for nine lenses, `.err` clean. +`output/task_05/*/checkpoint.hdf5` present for nine lenses, `.err` clean. ## Where to look -- `/mnt/c/Users/Jammy/Science/example/output/phase_05/` +- `/mnt/c/Users/Jammy/Science/example/output/task_05/` ## Runs - 342091_[0-8,10]: done — gpu — submitted 2026-08-30 — wall 6:12 - pulled_to: /mnt/c/Users/Jammy/Science/example/output/phase_05 + pulled_to: /mnt/c/Users/Jammy/Science/example/output/task_05 - 342091_9: failed — gpu — submitted 2026-08-30 — wall 0:00 — OOM before the first step - 342102: running — gpu — submitted 2026-09-01 — wall 0:00 — task 9 resubmitted alone after: 342091_9 @@ -63,24 +63,28 @@ sections are fixed: `## Question`, `## Witness`, `## Where to look`, `## Runs`, **`## Where to look` is rendered, not just parsed.** Its `- ` bullets are the answer to *which folder do I open* — the dashboard's `## By project` view and -`pyauto-brain cortex checkin` print them verbatim under the phase, and +`pyauto-brain cortex checkin` print them verbatim under the task, and `collect` resolves the ones that are absolute paths inside the project's roots to the artefacts it scores. `new` seeds the section with the placeholder -`- (the output path, once there is one)`, which is honest on a `planned` phase -and a hole on any other, so **`check` requires a phase whose `State:` is past +`- (the output path, once there is one)`, which is honest on a `planned` task +and a hole on any other, so **`check` requires a task whose `State:` is past `planned` to carry at least one bullet that is not that placeholder**. A bullet is free text: a path, a project-row-relative location, a Mind prompt, a GitHub ref — whatever a human would open. -### Phase header keys +A task has **no number**. Its identity is its **slug** — the file name, unique +per project by the path itself — because a science project is a set of ideas +run in whatever order the results dictate, not a sequence (decision, 2026-09-07). + +### Task header keys | Key | Value | Notes | |---|---|---| | `Project:` | project key | must equal the directory name **and** a `projects.yaml` key | -| `Phase:` | integer | unique per project; revival of a dropped phase is a new number | +| `Summary:` | at most ten words | **required** — the QUESTION the task answers, not its method or its run ids; the board's one line per task. No project name, no number | | `State:` | `planned \| gated \| ready \| submitted \| running \| pulled \| awaiting-ruling \| accepted \| rerun \| dropped` | | | `Gates:` | comma-separated GitHub refs | `Repo#N` (owner `PyAutoLabs`) or an issue/PR URL; **no `owner/Repo#N`** form | -| `Reset:` | reason | written by `move ready --reason ""` when a `submitted \| running` phase goes back to `ready` | +| `Reset:` | reason | written by `move ready --reason ""` when a `submitted \| running` task goes back to `ready` | | `Witness:` | free text | **mandatory before `submitted`** — the pre-registered checkable claim | | `Budget:` | `H+:MM` | wall budget per run | | `Runs:` | comma-separated job **stems** | the index of the `## Runs` body; equal to the set of body stems | @@ -88,7 +92,7 @@ GitHub ref — whatever a human would open. | `Review-minutes:` | integer | a seed, not a measurement | | `Epic:` | slug | OPTIONAL join key to a **Mind** epic; the Cortex keeps no epics file of its own | | `Filed:` | `YYYY-MM-DD` | | -| `Migrated-from:` | source path or ledger anchor | the Mind prompt, review file or project-ledger entry this phase was transcribed from (phase 4 of the birth epic) | +| `Migrated-from:` | source path or ledger anchor | the Mind prompt, review file or project-ledger entry this task was transcribed from (phase 4 of the birth epic) | Gate refs are matched by `GATE_REF_RE`, copied verbatim from `PyAutoMind/scripts/lifecycle.py`: @@ -106,7 +110,7 @@ The lookbehind `(? ready` — a human read `gates`, opened the refs and judged them cleared. No flag, and nothing written but `State:` | -| ready | gated | not an edge: re-gating a `ready` phase is a judgement, so it is a hand edit of the header | +| gated | ready | `move ready` — a human read `gates`, opened the refs and judged them cleared. No flag, and nothing written but `State:` | +| ready | gated | not an edge: re-gating a `ready` task is a judgement, so it is a hand edit of the header | | ready | submitted | `Witness:` non-empty AND `--run ` supplied | -| ready | pulled | legacy-born phase: every run line is `legacy\|legacy_wrong` (`new --legacy-run` / `move pulled`); `Witness:` still mandatory; every `legacy` run gets a `pulled_to:` (`--pulled-to`, else its own `where:`); refused when no run is `legacy` (nothing to review — `rule drop`) | +| ready | pulled | legacy-born task: every run line is `legacy\|legacy_wrong` (`new --legacy-run` / `move pulled`); `Witness:` still mandatory; every `legacy` run gets a `pulled_to:` (`--pulled-to`, else its own `where:`); refused when no run is `legacy` (nothing to review — `rule drop`) | | submitted / running | same | `--run ` appends a wave, a chained job or a checkpoint resubmit (`resumes:`); state unchanged | | submitted | running | — | | submitted / running | ready | no run line in `submitted\|running` AND ≥1 `failed\|timeout\|void`; `--reason` required (→ writes `Reset:`) | | running | pulled | no run line live; or `--partial` (a partial array), which `check` expects closed by a `leave-to-finish` ruling; `--pulled-to ` writes `pulled_to:` on every `done` run lacking one, and the move is refused when no `done` run would carry one | -| pulled | awaiting-ruling | — (the phase joins the rolling board) | +| pulled | awaiting-ruling | — (the task joins the rolling board) | | awaiting-ruling | accepted / rerun / dropped | **`rule` only** | | running / pulled / awaiting-ruling | same | `rule leave-to-finish` (state unchanged) | | accepted | rerun / dropped | `rule --supersedes ` only (the REWIND case) | | rerun | ready | — (the witness may be re-registered; run history is kept) | | any non-terminal | dropped | `rule drop` only | -| dropped | — | terminal (revival = a new phase number) | +| dropped | — | terminal (revival = a new slug) | `accepted` is **not** terminal: a later ruling may supersede the acceptance (2026-08-31's REWIND superseded accepted gates). `dropped` is. @@ -157,9 +161,11 @@ Offline invariants `check` enforces on top of the table: - `State = gated` ⇒ `Gates:` non-empty (a gate that does not exist cannot clear). - A header key outside the table is drift (`Gate:` must not pass as a silent - typo for `Gates:`); the five body sections are present; `Phase:` and - `Review-minutes:` are integers, `Budget:` is `H+:MM` and `Filed:` is - `YYYY-MM-DD`. A key with an empty value is read as absent. + typo for `Gates:`); the five body sections are present; `Summary:` is + present, non-empty and at most ten words; `Review-minutes:` is an integer, + `Budget:` is `H+:MM` and `Filed:` is `YYYY-MM-DD`. A key with an empty value + is read as absent. `Phase:` is named as a **retired** key rather than passing + as a generic unknown one — the number went on 2026-09-07. --- @@ -178,7 +184,7 @@ PyAutoCortex/ ├── dashboard.md ← GENERATED board — cortex conductor (`dashboard --apply`); LEDGER ├── dashboard.html ← GENERATED board, the Pages index; LEDGER │ -├── phases//.md ← one file per phase (LEDGER) +├── tasks//.md ← one file per task (LEDGER) ├── rulings/AGENTS.md ← the append-only rule ├── rulings///R--.md ← the ledger of record (LEDGER, append-only) ├── batches/AGENTS.md ← "closed history: never modified, only added" @@ -191,7 +197,7 @@ PyAutoCortex/ ├── scripts/cortex.py ← check · gates · rule · move · new · retire (stdlib + PyYAML) ├── scripts/ledger_merge.py ← the default-deny ledger classifier (+ append-only on rulings/, batches/) ├── tests/test_cortex.py tests/test_ledger_merge.py -├── tests/fixtures/skeleton/ ← one project, one phase per state, five rulings — the witness +├── tests/fixtures/skeleton/ ← one project, one task per state, five rulings — the witness ├── tests/fixtures/empty/ ← an empty map passes `check` │ ├── .github/workflows/cortex_check.yml ← check + pytest on push/PR @@ -223,7 +229,7 @@ structured facts on **indented continuation lines**, never in the note. - `` ∈ `submitted | running | done | failed | timeout | void | legacy | legacy_wrong`. `void` = cancelled or never produced a step. `legacy` / `legacy_wrong` = quarantine (reusable / not) — **a run state, - never a phase state**. + never a task state**. - `` — the SLURM partition the job went to, a bare word (`^[a-z][a-z0-9_-]*$`, e.g. `gpu`). The project's `partition:` row says which it may use. @@ -259,15 +265,15 @@ RUN_CONT_RE = re.compile( - every non-blank line under `## Runs` matches `RUN_LINE_RE` or `RUN_CONT_RE`; a continuation line follows a run line; -- task sets on one stem are **disjoint within a phase** (job ids are unique - per phase, not globally — one array may feed two phases); -- the `Runs:` header equals the set of body stems (both empty when the phase +- task sets on one stem are **disjoint within a task** (job ids are unique + per task, not globally — one array may feed two tasks); +- the `Runs:` header equals the set of body stems (both empty when the task has no runs); - `State: pulled` ⇒ at least one `done | legacy` run carries `pulled_to:`; - a `legacy | legacy_wrong` run carries `where:`; - an `after:` / `resumes:` target is the identifier (the text before the colon — `342091`, `342091_9` or `342091_[0-8,10]`) of another run line of - the **same phase**; + the **same task**; - a `ruled:` value resolves to a ruling file. --- @@ -277,13 +283,14 @@ RUN_CONT_RE = re.compile( `rulings///R--.md`. The id is `R-YYYYMMDD-nn` — a two-digit per-day sequence, global across projects; `rule` assigns it; the filename equals the id and so does the title line (`# ` or -`# — `). +`# — `); `rule` generates +`# — `. ````markdown -# R-20260901-02 — re-accept phase 8 with the corrected evidence pointer +# R-20260901-02 — re-accept example 08_accepted with the corrected evidence pointer Project: example -Phase: phases/example/08_accepted.md +Task: tasks/example/08_accepted.md Runs: 342050 Ruling: accept Supersedes: R-20260901-01 @@ -304,8 +311,8 @@ The human's words, verbatim. | Key | Value | Notes | |---|---|---| | `Project:` | project key | | -| `Phase:` | one phase path | repo-relative, `phases//.md` | -| `Runs:` | comma-separated stems, or empty | ⊆ the phase's `Runs:` | +| `Task:` | one task path | repo-relative, `tasks//.md` | +| `Runs:` | comma-separated stems, or empty | ⊆ the task's `Runs:` | | `Ruling:` | `accept \| rerun \| drop \| leave-to-finish` | the verb | | `Supersedes:` | one ruling id | optional; see the chain rules | | `Batch:` | `-` | optional-**historical**; the 2026-08/09 rulings' join, nothing writes new ones | @@ -316,8 +323,8 @@ The human's words, verbatim. Body: `## Ruling` — the human's words verbatim; `## Evidence` — pointers. -**One ruling file per phase.** A multi-phase ruling (a REWIND) is N `rule` -invocations with the same body — one per phase. +**One ruling file per task.** A multi-task ruling (a REWIND) is N `rule` +invocations with the same body — one per task. **Chain rules** (`check`): @@ -325,19 +332,19 @@ invocations with the same body — one per phase. the file's `/` directory; the body has `## Ruling` and `## Evidence`; - `Supersedes:` resolves to an existing ruling, is not the ruling itself, is - lexically smaller (earlier), and names the same project **and** phase; + lexically smaller (earlier), and names the same project **and** task; - **at most one successor per ruling** — a chain, not a tree: supersede the head; -- the phase's `Ruling:` is a chain head (no ruling supersedes it) whose - `Phase:` is that phase; -- the head's verb matches the phase's state: `accept ⇒ accepted`, +- the task's `Ruling:` is a chain head (no ruling supersedes it) whose + `Task:` is that task; +- the head's verb matches the task's state: `accept ⇒ accepted`, `drop ⇒ dropped`, `rerun ⇒ rerun | ready | submitted | running | pulled | - awaiting-ruling` (the phase has moved on), `leave-to-finish ⇒ any + awaiting-ruling` (the task has moved on), `leave-to-finish ⇒ any non-terminal state`; -- the ruling's `Runs:` ⊆ the phase's runs; +- the ruling's `Runs:` ⊆ the task's runs; - `Batch:`, when present, names an existing `batches/.md`. -A phase may hold rulings that are not chained to each other — a +A task may hold rulings that are not chained to each other — a `leave-to-finish` followed by an `accept` is two chains of one. `Supersedes:` is for replacing a verdict, not for sequencing. @@ -396,11 +403,11 @@ listed here. A document PyYAML cannot read is reported as one problem. the one verb that writes this file: it flips the row's `status:` to `retired` and rewrites (or appends) its `note:` as `"retired : "`. The **row stays** — it is the only record of - where that project's data lives — and no phase or ruling is touched; + where that project's data lives — and no task or ruling is touched; `rulings/` is append-only and a change of status is not a rewrite of - history. It refuses while the project holds a phase in any state + history. It refuses while the project holds a task in any state outside `accepted | rerun | dropped | planned`, naming each one, so the - live questions are ruled or dropped first; `planned` phases are unasked + live questions are ruled or dropped first; `planned` tasks are unasked questions and may stay. The edit is exactly those two lines — every other byte of the file is preserved — and the result is re-parsed before it is kept, the original bytes restored if it does not read @@ -422,7 +429,7 @@ deny**: | Ledger — merged automatically | Code — always a human | |---|---| -| `phases/**`, `rulings/**`, `batches/**` | `scripts/`, `tests/`, `.github/`, `policy/`, `docs/` | +| `tasks/**`, `rulings/**`, `batches/**` | `scripts/`, `tests/`, `.github/`, `policy/`, `docs/` | | `checkin.yaml` | `projects.yaml`, `README.md`, `AGENTS.md`, `REFERENCE.md`, … | | `dashboard.md`, `dashboard.html` (generated) | **`AGENTS.md` / `TEMPLATE.md` inside a ledger dir** | | | anything unclassified — a new root file, a new top-level folder | @@ -438,7 +445,7 @@ behaviour. Auto-merging a rewrite of `rulings/AGENTS.md` would let a branch edit the rule that governs its own merge. Pulling the other way, the two **generated** board pages are ledger: a branch -that moves a phase re-renders them in the same push, and +that moves a task re-renders them in the same push, and `dashboard_refresh.yml`'s self-heal commit has to land without a human. And a fourth Cortex rule on *kind* rather than path: `ledger_merge.py` @@ -467,27 +474,27 @@ Stdlib only; `main(argv)`; no import-time side effects; every verb takes `--root ` (default: the repo root the script lives in). Every leg of `check` takes `root: Path`, so tests run it against a `tmp_path` copy. -- **`check`** — every rule in this file: phase headers and states, the witness +- **`check`** — every rule in this file: task headers and states, the witness invariant, `## Where to look` naming somewhere past `planned`, run lines, ruling ids and chains, the verb↔state agreement, every project named by a - phase path is a `projects.yaml` key, and `projects.yaml`'s own fields. Hermetic (no network, no git). Output in `lifecycle.py`'s shape — + task path is a `projects.yaml` key, and `projects.yaml`'s own fields. Hermetic (no network, no git). Output in `lifecycle.py`'s shape — `cortex check: OK` or `cortex check: DRIFT` followed by one ` - …` line per finding; exit 1 on drift. -- **`gates`** — read-only and offline: every `gated` phase, its refs and the +- **`gates`** — read-only and offline: every `gated` task, its refs and the URL each ref resolves to (`GATE_REF_RE` → `gate_url`). Nothing polls GitHub and nothing flips a state. Grading was retired on 2026-09-03 — in its whole life it saw 2 gated refs and flipped 0, while schema decision 54 routes sequencing through prose `Ready when:` lines. A human reads the listing, - opens the refs and types `move ready`. -- **`rule --body [--supersedes ] [--batch ] + opens the refs and types `move ready`. +- **`rule --body [--supersedes ] [--batch ] [--minutes n] [--follow-up ]...`** — assigns the next id for today, - writes the ruling file, updates the phase's `Ruling:` and `State:` per the - table (and appends ` — ` to the phase's - `## Ruling`); the ruling's `Runs:` is the phase's, its `## Evidence` is the - phase's `## Where to look`; refuses to touch an existing ruling; refuses a - verb the table does not allow from the phase's state; validates everything + writes the ruling file, updates the task's `Ruling:` and `State:` per the + table (and appends ` — ` to the task's + `## Ruling`); the ruling's `Runs:` is the task's, its `## Evidence` is the + task's `## Where to look`; refuses to touch an existing ruling; refuses a + verb the table does not allow from the task's state; validates everything before writing anything. -- **`move [--run ] [--reason ..] +- **`move [--run ] [--reason ..] [--partial] [--pulled-to ] [--partition ..] [--after ] [--resumes ] [--note ..]`** — the table; refuses every ruling edge with a message naming `rule`. `--run` on `submitted`/`running` appends a @@ -497,23 +504,24 @@ Stdlib only; `main(argv)`; no import-time side effects; every verb takes partition is the project's `partition:` row unless that is `both`, when `--partition` is required. Every edit is an in-place header edit or an appended run line; every other byte of the file is preserved. -- **`new --phase [--gates ..] [--epic ..] +- **`new --summary "<≤10 words>" [--gates ..] [--epic ..] [--legacy-run ]... [--legacy-wrong ]... [--where ] [--partition ..] [--witness ..] [--budget ..] [--minutes n] [--title ..]`** - — writes `phases//.md` from the template in `planned` (or + — writes `tasks//.md` from the template in `planned` (or `ready` when `--legacy-run` / `--legacy-wrong` is given and every run is legacy); each legacy run line is written with today's date, `wall 0:00` and the note `pre-Cortex run, migrated` — the human corrects the date and wall by hand — and `--where` (required) as its `where:`; a legacy-born - phase refuses `--gates`. Refuses a duplicate phase number, an existing - file or an unknown project. + task refuses `--gates`. `--summary` is required and held to the same ten + words `check` holds it to. Refuses an existing file (the slug is the + identity — there is nothing else to collide on) or an unknown project. - **`retire --why ""`** — the only verb that writes `projects.yaml`: `status: retired` plus a `note:` reading `"retired : "`, two lines edited in place and every other byte preserved. Refuses an unknown key, an already-retired row, and any - project still holding a phase outside `accepted | rerun | dropped | - planned` (the message names each ` — `). The row, its - phases and its rulings all stay; see `projects.yaml` above. + project still holding a task outside `accepted | rerun | dropped | + planned` (the message names each ` — `). The row, its + tasks and its rulings all stay; see `projects.yaml` above. --- @@ -531,11 +539,11 @@ pyauto-brain cortex checkin --apply --skip-pull # re-score what is already her ``` It sweeps every `status: active` row of `projects.yaml`, plus any project that -owns a phase in `submitted | running`, and runs **that project's own** +owns a task in `submitted | running`, and runs **that project's own** `/ pull` — the verb all seven implement. Nothing else reaches a cluster; the conductor adds no SSH. A pull that exits non-zero is recorded against its project and the sweep continues. Then it scores every live -phase (the six legs below), moves `running → pulled → awaiting-ruling`, +task (the six legs below), moves `running → pulled → awaiting-ruling`, re-renders `dashboard.md` + `dashboard.html`, and prints a summary keyed **by project** — the last thing on screen, so a chat sees it first. @@ -566,13 +574,13 @@ carrying pull results and six-leg health). One builder, three renderings — a prompt added to a state appears in all of them. Per project: the three folders it lives in (`local_path`, `mirror` when it has -one, `ral_root`), what came of its pull, its phase counts by state, then every -phase that is still **open** — awaiting a ruling, still out there, ready to +one, `ral_root`), what came of its pull, its task counts by state, then every +task that is still **open** — awaiting a ruling, still out there, ready to submit, gated, planned — each with its state and health, its `## Where to look` bullets verbatim, and a copy chip per prompt its state carries. `accepted`, -`rerun` and `dropped` phases are history and appear only in the counts. A +`rerun` and `dropped` tasks are history and appear only in the counts. A project gets a block when its `projects.yaml` row is `status: active`, or when -it still holds an open phase; every other project folds into one line. +it still holds an open task; every other project folds into one line. On the board `## By project` is the **first** section, above the state lists: those are cross-project, so every row has to be read to find out whose it is, @@ -582,31 +590,31 @@ strip itself is unchanged (the Brain board reads it). ### The five starting prompts -Every phase row hands the reader a command rather than a decision. Which -prompts a phase carries is its **state**, and nothing else: +Every task row hands the reader a command rather than a decision. Which +prompts a task carries is its **state**, and nothing else: | Prompt | States | What it says | |---|---|---| -| rule on it | `pulled`, `awaiting-ruling` | read the witness and the pulled evidence, draft the ruling body, `rule --body ` | -| the results are good — accept and open phase N+1 | `pulled`, `awaiting-ruling` | `rule accept --body `, then open the next phase (below) | -| run it again | `pulled`, `awaiting-ruling`, `running` | `rule rerun --body `, `move ready`, the project's own submit, `move submitted --run ` | +| rule on it | `pulled`, `awaiting-ruling` | read the witness and the pulled evidence, draft the ruling body, `rule --body ` | +| the results are good — accept and open the next task | `pulled`, `awaiting-ruling` | `rule accept --body `, then open the next task (below) | +| run it again | `pulled`, `awaiting-ruling`, `running` | `rule rerun --body `, `move ready`, the project's own submit, `move submitted --run ` | | where the jobs stand | `submitted`, `running` | the project's own ` jobs` | -| submit it | `ready` | the phase, the project's own submit verb, `move submitted --run ` | +| submit it | `ready` | the task, the project's own submit verb, `move submitted --run ` | -(`gated` and `planned` phases carry the one-line `gates` / `move … ready` +(`gated` and `planned` tasks carry the one-line `gates` / `move … ready` command they always had.) -**Accept and open phase N+1** is two commands in one prompt because they are -one decision: an accept that opens nothing leaves the programme where it was. -Its second half prefills from the tree — when the project already holds a -phase numbered N+1 in `planned` or `gated`, the prompt opens *that* phase -(`move … ready` + the launch) rather than writing a second one beside it; -when N+1 is taken by a phase that has already run, it says so and offers the -lowest free number instead. The `new` line carries the phase's own `Epic:` and -spells out the title rule: **`new` writes `# — phase N: ` in front -of `--title` itself**, so `--title` takes the tail alone. - -**Run it again** is a ruling first: `rerun` is the only verb that puts a phase +**Accept and open the next task** is two commands in one prompt because they +are one decision: an accept that opens nothing leaves the programme where it +was. With the number gone there is no "N+1" to prefill — the tasks a project +holds are unordered ideas, so the second half offers the project's `planned` +and `gated` tasks to open (`move … ready` + the launch) and otherwise a `new` +line for a task the human names with a slug and a `Summary:`. The `new` line +carries the task's own `Epic:` and spells out the title rule: **`new` writes +`# — ` in front of `--title` itself**, so `--title` takes the tail +alone. + +**Run it again** is a ruling first: `rerun` is the only verb that puts a task back on the board, so the verdict on what came back is what makes the next submission a rerun rather than a repeat. Its `move … submitted --run` line carries the reminder that it is one run per call (`--after ` chains the @@ -628,7 +636,7 @@ the same file `collect`'s checkpoint leg reads: | `pulled_at` | the check-in | UTC `YYYY-MM-DDTHH:MMZ` of the pull that just finished | | `cmd` | the check-in | the pull as a human would type it (`cd && pull`) | | `rc` | the check-in | that pull's exit code — `0`, or the manifest is not written | -| `phases_live` | the check-in | the phase paths that were `submitted \| running` when it ran | +| `tasks_live` | the check-in | the task paths that were `submitted \| running` when it ran | | `schema` | a project's own CLI | `1` where the CLI writes the richer shape; absent = the older `runs`-only one | | `checkpoints` | a project's own CLI | `{"": {"bytes": N, "mtime": ISO}}` | | `runs` | a project's own CLI | `{"": {"checkpoint_bytes": N, "checkpoint_mtime": ISO}}` | @@ -648,7 +656,7 @@ The manifests live **outside** this repository, in the science trees, so `--push` is allowed only when **`gh auth status` succeeds** *and* **this checkout is clean on `main`** — checked before anything is written, since -"clean" stops being true the moment the phases move. That is the cloud/laptop +"clean" stops being true the moment the tasks move. That is the 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-` from a fresh `origin/main`, commits the changed paths explicitly and pushes; @@ -665,14 +673,14 @@ check-in — is the Brain's **cortex conductor**: ```bash pyauto-brain cortex checkin [--dry-run|--apply] [--push|--no-push] [--project KEY] [--skip-pull] [--refreshed ISO] pyauto-brain cortex census [--json] # what is held, by state -pyauto-brain cortex census --by-project # ... by project: folders, open phases, prompts +pyauto-brain cortex census --by-project # ... by project: folders, open tasks, prompts pyauto-brain cortex dashboard --check | --apply # render the two pages -pyauto-brain cortex gates # the gated phases and their refs -pyauto-brain cortex collect [--phase REL] [--pull] [--refreshed ISO] [--apply] [--out F] +pyauto-brain cortex gates # the gated tasks and their refs +pyauto-brain cortex collect [--task REL] [--pull] [--refreshed ISO] [--apply] [--out F] ``` -`checkin` composes the others (above). `collect` with no `--phase` scopes to -**every** phase in `submitted | running` — the scorer on its own. +`checkin` composes the others (above). `collect` with no `--task` scopes to +**every** task in `submitted | running` — the scorer on its own. With no Brain install, or from a workflow: @@ -686,7 +694,7 @@ verb. (`--cortex . dashboard --check` exits 2.) The root is resolved **Two spellings of the same flag.** The conductor writes with `--apply` (the Brain's house spelling); `scripts/cortex.py` writes with `--write`. Every edit -to a phase is `cortex.py`'s either way. +to a task is `cortex.py`'s either way. **The `--check` exit-code contract** (`dashboard_refresh.yml` depends on it): @@ -705,7 +713,7 @@ for exactly that reason. | Workflow | Trigger | Writes | |---|---|---| -| `cortex_check.yml` | push/PR on `phases/ rulings/ batches/ projects.yaml checkin.yaml dashboard.* scripts/ tests/` | nothing (`cortex.py check` + pytest) | +| `cortex_check.yml` | push/PR on `tasks/ rulings/ batches/ projects.yaml checkin.yaml dashboard.* scripts/ tests/` | nothing (`cortex.py check` + pytest) | | `dashboard_refresh.yml` | push to main + PR on the ledger paths and the two pages, nightly **03:35 UTC**, dispatch | `dashboard.md`, `dashboard.html` on main (3-attempt fetch/reset/render/commit/push); a PR run errors instead of healing | | `pages_dashboard.yml` | push to `dashboard.html`, dispatch | nothing in git — publishes `dashboard.html` as the Pages index | | `ledger_merge.yml` | push to `claude/**`, dispatch | merges a ledger-only branch into main | diff --git a/batches/reviews/AGENTS.md b/batches/reviews/AGENTS.md index 4f2b809..9f4ef4e 100644 --- a/batches/reviews/AGENTS.md +++ b/batches/reviews/AGENTS.md @@ -7,5 +7,5 @@ the Cortex does not exist. Never modified, only added. Nothing writes new ones. The review-slot apparatus these came from — the packet page, the scored members, the `-r` sittings, the review-minute budget — was retired on 2026-09-03; the human now rules in the session where the results are -read, and `scripts/cortex.py rule --body ` puts the words +read, and `scripts/cortex.py rule --body ` puts the words into `rulings/`, which is the ledger of record. diff --git a/docs/schema_decisions.md b/docs/schema_decisions.md index 6ef07fc..b127044 100644 --- a/docs/schema_decisions.md +++ b/docs/schema_decisions.md @@ -401,3 +401,28 @@ Choices made in phase 2 — the conductor, the board and the workflows PyYAML-parity test proved it redundant. What the human actually does is check in on the runs, so the apparatus that modelled them as a reviewer working a scheduled shift is cost with no reader. + +59. **Tasks, not phases: `phases/` → `tasks/`, the number is deleted, a + ten-word `Summary:` is required** (2026-09-07, PyAutoCortex#20). The tree + is `tasks//.md`; the `Phase: N` header is gone outright, + with no deprecated shadow — `check` names it as a retired key rather than + letting it pass as a generic unknown one. A task's identity is its + **slug**, already unique per project by the path. Titles are + `# — `; ruling titles `# <R-id> — <verb> <project> + <slug>`; a ruling's head field is `Task:` pointing at the `tasks/…` path; + "revival of a dropped task is a new number" becomes "a new slug"; + `new <project> <slug>` loses `--phase` and gains a required `--summary`. + Every task carries `Summary:` directly after `Project:` — at most ten + words, the **question** the task answers, not its method and not its run + ids — and `check` enforces presence and the cap. + Why: science is a set of unordered ideas, run in whatever order results + and priorities dictate; "phase" implies a sequence that does not exist, + and the number carried nothing the slug did not already carry — no + ordering was ever read off it, and a dropped task's revival needed a + fresh number for no reason. The `Summary:` is the other half of the same + decision: the board lists every open task of a project on one line each, + so each task needs one line that is readable at a glance, and the + rendered title ("Inference_programme — phase 11: cluster extended-source + inference — gradient-based fitting building on JAX knowledge") was not + it. Mind **epics** keep their numbered phases: development work really is + sequential, and `Epic:` on a task file is unchanged. diff --git a/projects.yaml b/projects.yaml index ed6cb0f..b611d38 100644 --- a/projects.yaml +++ b/projects.yaml @@ -36,7 +36,7 @@ # THE WORKSPACE-PATHS EXCEPTION. `local_path` and `mirror` are absolute laptop # paths OUTSIDE the PyAutoLabs workspace — the Science folder under /mnt/c/… # where the datasets, output trees and pulled results live. This file is the -# one place in the organism that carries such a path; a phase file or a ruling +# one place in the organism that carries such a path; a task file or a ruling # reaches a project through its row here, never with a bare path of its own. # --------------------------------------------------------------------------- # @@ -79,7 +79,7 @@ euclid: witness_file: catalogue/**/*.csv partition: both status: dormant - note: "NO PyAutoLabs remote by ruling 2026-09-01: tracks DR1 tile-keyed catalogue CSV, personal remote Jammy2211/euclid-dr1-modeling, nested Overleaf paper repo, 34 GB; code half = PyAutoLabs/euclid_strong_lens_modeling_pipeline; witness undefined until a science phase pre-registers one; dormant 2026-09-07: superseded by euclid_dr1_prelim, which phase 4 births; local output/ cleared 2026-09-07 (60 GB of May–June DR1 grade_ab runs, still on RAL under /mnt/ral/jnightin/euclid)" + note: "NO PyAutoLabs remote by ruling 2026-09-01: tracks DR1 tile-keyed catalogue CSV, personal remote Jammy2211/euclid-dr1-modeling, nested Overleaf paper repo, 34 GB; code half = PyAutoLabs/euclid_strong_lens_modeling_pipeline; witness undefined until a science task pre-registers one; dormant 2026-09-07: superseded by euclid_dr1_prelim, which phase 4 births; local output/ cleared 2026-09-07 (60 GB of May–June DR1 grade_ab runs, still on RAL under /mnt/ral/jnightin/euclid)" euclid_dr1_prelim: remote: none @@ -92,7 +92,7 @@ euclid_dr1_prelim: witness_file: results/**/*.json partition: both status: active - note: "created by euclid-dr1-prep phase 4 (a Cortex phase after migration); every path is the planned one; active 2026-09-07: phase 4 launched (jobs 342301_[0-9])" + note: "created by euclid-dr1-prep phase 4 (a Cortex task after migration); every path is the planned one; active 2026-09-07: phase 4 launched (jobs 342301_[0-9])" # --------------------------------------------------------------------------- # # dormant — git repos only. The non-git trees (subhalo, euclid_group, z_*) are diff --git a/rulings/2026/08/R-20260831-01.md b/rulings/2026/08/R-20260831-01.md index 301c73d..c2d10ef 100644 --- a/rulings/2026/08/R-20260831-01.md +++ b/rulings/2026/08/R-20260831-01.md @@ -1,7 +1,7 @@ -# R-20260831-01 — drop inference_programme phase 1 +# R-20260831-01 — drop inference_programme rewind_2026_08_31 Project: inference_programme -Phase: phases/inference_programme/rewind_2026_08_31.md +Task: tasks/inference_programme/rewind_2026_08_31.md Runs: 341908, 341874 Ruling: drop Reviewed-at: 2026-08-31T13:34Z diff --git a/rulings/2026/08/R-20260831-02.md b/rulings/2026/08/R-20260831-02.md index ce5a36c..b317f1f 100644 --- a/rulings/2026/08/R-20260831-02.md +++ b/rulings/2026/08/R-20260831-02.md @@ -1,7 +1,7 @@ -# R-20260831-02 — drop inference_programme phase 2 +# R-20260831-02 — drop inference_programme smc_probe_342018 Project: inference_programme -Phase: phases/inference_programme/smc_probe_342018.md +Task: tasks/inference_programme/smc_probe_342018.md Runs: 342018 Ruling: drop Batch: 2026-08-31-am diff --git a/rulings/2026/08/R-20260831-03.md b/rulings/2026/08/R-20260831-03.md index d28e1af..fa867c8 100644 --- a/rulings/2026/08/R-20260831-03.md +++ b/rulings/2026/08/R-20260831-03.md @@ -1,7 +1,7 @@ -# R-20260831-03 — drop inference_programme phase 3 +# R-20260831-03 — drop inference_programme knn_rerun_342016_7 Project: inference_programme -Phase: phases/inference_programme/knn_rerun_342016_7.md +Task: tasks/inference_programme/knn_rerun_342016_7.md Runs: 342016 Ruling: drop Batch: 2026-08-31-am diff --git a/rulings/2026/08/R-20260831-04.md b/rulings/2026/08/R-20260831-04.md index a2585b5..ff0171f 100644 --- a/rulings/2026/08/R-20260831-04.md +++ b/rulings/2026/08/R-20260831-04.md @@ -1,7 +1,7 @@ -# R-20260831-04 — drop inference_programme phase 4 +# R-20260831-04 — drop inference_programme refs_5_6_342016_56 Project: inference_programme -Phase: phases/inference_programme/refs_5_6_342016_56.md +Task: tasks/inference_programme/refs_5_6_342016_56.md Runs: 342016 Ruling: drop Batch: 2026-08-31-am diff --git a/rulings/2026/08/R-20260831-05.md b/rulings/2026/08/R-20260831-05.md index 326d662..a85f779 100644 --- a/rulings/2026/08/R-20260831-05.md +++ b/rulings/2026/08/R-20260831-05.md @@ -1,7 +1,7 @@ -# R-20260831-05 — drop inference_programme phase 5 +# R-20260831-05 — drop inference_programme slogdet_ab_342017 Project: inference_programme -Phase: phases/inference_programme/slogdet_ab_342017.md +Task: tasks/inference_programme/slogdet_ab_342017.md Runs: 342017 Ruling: drop Batch: 2026-08-31-am diff --git a/rulings/2026/08/R-20260831-06.md b/rulings/2026/08/R-20260831-06.md index c6819c5..11bde70 100644 --- a/rulings/2026/08/R-20260831-06.md +++ b/rulings/2026/08/R-20260831-06.md @@ -1,7 +1,7 @@ -# R-20260831-06 — accept subhalo_validation phase 1 +# R-20260831-06 — accept subhalo_validation delaunay_adapt_split_pl_sersic_0 Project: subhalo_validation -Phase: phases/subhalo_validation/delaunay_adapt_split_pl_sersic_0.md +Task: tasks/subhalo_validation/delaunay_adapt_split_pl_sersic_0.md Runs: 342027 Ruling: accept Batch: 2026-08-31-am diff --git a/rulings/2026/08/R-20260831-07.md b/rulings/2026/08/R-20260831-07.md index e73b6c1..e3ff399 100644 --- a/rulings/2026/08/R-20260831-07.md +++ b/rulings/2026/08/R-20260831-07.md @@ -1,7 +1,7 @@ -# R-20260831-07 — leave-to-finish subhalo_validation phase 2 +# R-20260831-07 — leave-to-finish subhalo_validation delaunay_adapt_split_pl_eff_0 Project: subhalo_validation -Phase: phases/subhalo_validation/delaunay_adapt_split_pl_eff_0.md +Task: tasks/subhalo_validation/delaunay_adapt_split_pl_eff_0.md Runs: 342027 Ruling: leave-to-finish Batch: 2026-08-31-am diff --git a/rulings/2026/08/R-20260831-08.md b/rulings/2026/08/R-20260831-08.md index 205bfe4..ceaf5df 100644 --- a/rulings/2026/08/R-20260831-08.md +++ b/rulings/2026/08/R-20260831-08.md @@ -1,7 +1,7 @@ -# R-20260831-08 — leave-to-finish subhalo_validation phase 3 +# R-20260831-08 — leave-to-finish subhalo_validation delaunay_adapt_split_pl_eff_1_outer Project: subhalo_validation -Phase: phases/subhalo_validation/delaunay_adapt_split_pl_eff_1_outer.md +Task: tasks/subhalo_validation/delaunay_adapt_split_pl_eff_1_outer.md Runs: 342027, 342093 Ruling: leave-to-finish Batch: 2026-08-31-am diff --git a/rulings/2026/09/R-20260901-01.md b/rulings/2026/09/R-20260901-01.md index 3cbe3c9..a330bbb 100644 --- a/rulings/2026/09/R-20260901-01.md +++ b/rulings/2026/09/R-20260901-01.md @@ -1,7 +1,7 @@ -# R-20260901-01 — accept inference_programme phase 7 +# R-20260901-01 — accept inference_programme mge_pos_ref_reuse Project: inference_programme -Phase: phases/inference_programme/mge_pos_ref_reuse.md +Task: tasks/inference_programme/mge_pos_ref_reuse.md Runs: 340210 Ruling: accept Batch: 2026-08-31-pm diff --git a/rulings/2026/09/R-20260901-02.md b/rulings/2026/09/R-20260901-02.md index 55bfddc..04961ff 100644 --- a/rulings/2026/09/R-20260901-02.md +++ b/rulings/2026/09/R-20260901-02.md @@ -1,7 +1,7 @@ -# R-20260901-02 — accept inference_programme phase 8 +# R-20260901-02 — accept inference_programme mge_fp64_retro_baseline Project: inference_programme -Phase: phases/inference_programme/mge_fp64_retro_baseline.md +Task: tasks/inference_programme/mge_fp64_retro_baseline.md Runs: 339070 Ruling: accept Batch: 2026-08-31-pm diff --git a/rulings/2026/09/R-20260901-03.md b/rulings/2026/09/R-20260901-03.md index 92c0db7..629ba58 100644 --- a/rulings/2026/09/R-20260901-03.md +++ b/rulings/2026/09/R-20260901-03.md @@ -1,7 +1,7 @@ -# R-20260901-03 — drop inference_programme phase 9 +# R-20260901-03 — drop inference_programme delaunay_fp64_retro_baseline Project: inference_programme -Phase: phases/inference_programme/delaunay_fp64_retro_baseline.md +Task: tasks/inference_programme/delaunay_fp64_retro_baseline.md Runs: 339071 Ruling: drop Batch: 2026-08-31-pm diff --git a/rulings/2026/09/R-20260901-04.md b/rulings/2026/09/R-20260901-04.md index 6946549..651469f 100644 --- a/rulings/2026/09/R-20260901-04.md +++ b/rulings/2026/09/R-20260901-04.md @@ -1,7 +1,7 @@ -# R-20260901-04 — drop inference_programme phase 6 +# R-20260901-04 — drop inference_programme failed_submissions_342008_10 Project: inference_programme -Phase: phases/inference_programme/failed_submissions_342008_10.md +Task: tasks/inference_programme/failed_submissions_342008_10.md Runs: 342008, 342009, 342010 Ruling: drop Reviewed-at: 2026-09-01T15:13Z diff --git a/rulings/2026/09/R-20260902-01.md b/rulings/2026/09/R-20260902-01.md index 75801b5..0d1ef95 100644 --- a/rulings/2026/09/R-20260902-01.md +++ b/rulings/2026/09/R-20260902-01.md @@ -1,7 +1,7 @@ -# R-20260902-01 — accept inference_programme phase 10 +# R-20260902-01 — accept inference_programme inference_refs_v1_redo Project: inference_programme -Phase: phases/inference_programme/inference_refs_v1_redo.md +Task: tasks/inference_programme/inference_refs_v1_redo.md Runs: 342091 Ruling: accept Reviewed-at: 2026-09-02T15:52Z diff --git a/rulings/2026/09/R-20260902-02.md b/rulings/2026/09/R-20260902-02.md index 8e6f09d..ed2de9d 100644 --- a/rulings/2026/09/R-20260902-02.md +++ b/rulings/2026/09/R-20260902-02.md @@ -1,7 +1,7 @@ -# R-20260902-02 — accept subhalo_validation phase 2 +# R-20260902-02 — accept subhalo_validation delaunay_adapt_split_pl_eff_0 Project: subhalo_validation -Phase: phases/subhalo_validation/delaunay_adapt_split_pl_eff_0.md +Task: tasks/subhalo_validation/delaunay_adapt_split_pl_eff_0.md Runs: 342027 Ruling: accept Reviewed-at: 2026-09-02T16:00Z diff --git a/rulings/2026/09/R-20260902-03.md b/rulings/2026/09/R-20260902-03.md index a757b3a..5f39233 100644 --- a/rulings/2026/09/R-20260902-03.md +++ b/rulings/2026/09/R-20260902-03.md @@ -1,7 +1,7 @@ -# R-20260902-03 — accept subhalo_validation phase 4 +# R-20260902-03 — accept subhalo_validation rectangular_adapt_pl_sersic_0 Project: subhalo_validation -Phase: phases/subhalo_validation/rectangular_adapt_pl_sersic_0.md +Task: tasks/subhalo_validation/rectangular_adapt_pl_sersic_0.md Runs: 342094, 342095 Ruling: accept Reviewed-at: 2026-09-02T16:07Z diff --git a/rulings/2026/09/R-20260902-04.md b/rulings/2026/09/R-20260902-04.md index acf9c00..b4435c9 100644 --- a/rulings/2026/09/R-20260902-04.md +++ b/rulings/2026/09/R-20260902-04.md @@ -1,7 +1,7 @@ -# R-20260902-04 — rerun subhalo_validation phase 3 +# R-20260902-04 — rerun subhalo_validation delaunay_adapt_split_pl_eff_1_outer Project: subhalo_validation -Phase: phases/subhalo_validation/delaunay_adapt_split_pl_eff_1_outer.md +Task: tasks/subhalo_validation/delaunay_adapt_split_pl_eff_1_outer.md Runs: 342027, 342093 Ruling: rerun Reviewed-at: 2026-09-02T16:40Z diff --git a/rulings/2026/09/R-20260902-05.md b/rulings/2026/09/R-20260902-05.md index 5f3fb0a..8b51677 100644 --- a/rulings/2026/09/R-20260902-05.md +++ b/rulings/2026/09/R-20260902-05.md @@ -1,7 +1,7 @@ -# R-20260902-05 — rerun subhalo_validation phase 2 +# R-20260902-05 — rerun subhalo_validation delaunay_adapt_split_pl_eff_0 Project: subhalo_validation -Phase: phases/subhalo_validation/delaunay_adapt_split_pl_eff_0.md +Task: tasks/subhalo_validation/delaunay_adapt_split_pl_eff_0.md Runs: 342027 Ruling: rerun Supersedes: R-20260902-02 diff --git a/rulings/2026/09/R-20260902-06.md b/rulings/2026/09/R-20260902-06.md index 55d4292..a2efe9e 100644 --- a/rulings/2026/09/R-20260902-06.md +++ b/rulings/2026/09/R-20260902-06.md @@ -1,7 +1,7 @@ -# R-20260902-06 — rerun subhalo_validation phase 4 +# R-20260902-06 — rerun subhalo_validation rectangular_adapt_pl_sersic_0 Project: subhalo_validation -Phase: phases/subhalo_validation/rectangular_adapt_pl_sersic_0.md +Task: tasks/subhalo_validation/rectangular_adapt_pl_sersic_0.md Runs: 342094, 342095 Ruling: rerun Supersedes: R-20260902-03 diff --git a/rulings/2026/09/R-20260902-07.md b/rulings/2026/09/R-20260902-07.md index 1e70337..c49b432 100644 --- a/rulings/2026/09/R-20260902-07.md +++ b/rulings/2026/09/R-20260902-07.md @@ -1,7 +1,7 @@ -# R-20260902-07 — rerun subhalo_validation phase 1 +# R-20260902-07 — rerun subhalo_validation delaunay_adapt_split_pl_sersic_0 Project: subhalo_validation -Phase: phases/subhalo_validation/delaunay_adapt_split_pl_sersic_0.md +Task: tasks/subhalo_validation/delaunay_adapt_split_pl_sersic_0.md Runs: 342027 Ruling: rerun Supersedes: R-20260831-06 diff --git a/rulings/2026/09/R-20260902-08.md b/rulings/2026/09/R-20260902-08.md index 8997f36..24984f5 100644 --- a/rulings/2026/09/R-20260902-08.md +++ b/rulings/2026/09/R-20260902-08.md @@ -1,7 +1,7 @@ -# R-20260902-08 — accept inference_programme phase 13 +# R-20260902-08 — accept inference_programme phase2_nss_mainline_gate_a_reuse Project: inference_programme -Phase: phases/inference_programme/phase2_nss_mainline_gate_a_reuse.md +Task: tasks/inference_programme/phase2_nss_mainline_gate_a_reuse.md Runs: 338491, 338492, 338493, 338870, 338871, 338872, 338873, 339067, 339068, 339069, 339070, 339071 Ruling: accept Batch: 2026-09-02-pm diff --git a/rulings/2026/09/R-20260902-09.md b/rulings/2026/09/R-20260902-09.md index 2b2894a..9b9650c 100644 --- a/rulings/2026/09/R-20260902-09.md +++ b/rulings/2026/09/R-20260902-09.md @@ -1,7 +1,7 @@ -# R-20260902-09 — accept inference_programme phase 14 +# R-20260902-09 — accept inference_programme phase3_prodigy_reliability_gate_b1_reuse Project: inference_programme -Phase: phases/inference_programme/phase3_prodigy_reliability_gate_b1_reuse.md +Task: tasks/inference_programme/phase3_prodigy_reliability_gate_b1_reuse.md Runs: 338523, 338524, 338525, 338526, 339065, 339066, 339070 Ruling: accept Batch: 2026-09-02-pm diff --git a/rulings/2026/09/R-20260902-10.md b/rulings/2026/09/R-20260902-10.md index a22971b..6cf9dc7 100644 --- a/rulings/2026/09/R-20260902-10.md +++ b/rulings/2026/09/R-20260902-10.md @@ -1,7 +1,7 @@ -# R-20260902-10 — accept inference_programme phase 15 +# R-20260902-10 — accept inference_programme phase4_positions_gate_b2_reuse Project: inference_programme -Phase: phases/inference_programme/phase4_positions_gate_b2_reuse.md +Task: tasks/inference_programme/phase4_positions_gate_b2_reuse.md Runs: 338525, 340114, 340115, 341892 Ruling: accept Batch: 2026-09-02-pm diff --git a/rulings/2026/09/R-20260904-01.md b/rulings/2026/09/R-20260904-01.md index c26dcd4..537f3af 100644 --- a/rulings/2026/09/R-20260904-01.md +++ b/rulings/2026/09/R-20260904-01.md @@ -1,7 +1,7 @@ -# R-20260904-01 — accept inference_programme phase 12 +# R-20260904-01 — accept inference_programme refs_v1_positions_on_completion Project: inference_programme -Phase: phases/inference_programme/refs_v1_positions_on_completion.md +Task: tasks/inference_programme/refs_v1_positions_on_completion.md Runs: 342241 Ruling: accept Reviewed-at: 2026-09-04T15:32Z diff --git a/rulings/2026/09/R-20260904-02.md b/rulings/2026/09/R-20260904-02.md index 0c77e0b..6610388 100644 --- a/rulings/2026/09/R-20260904-02.md +++ b/rulings/2026/09/R-20260904-02.md @@ -1,7 +1,7 @@ -# R-20260904-02 — drop inference_programme phase 16 +# R-20260904-02 — drop inference_programme phase5_mesh_gradient_positions_on Project: inference_programme -Phase: phases/inference_programme/phase5_mesh_gradient_positions_on.md +Task: tasks/inference_programme/phase5_mesh_gradient_positions_on.md Runs: Ruling: drop Reviewed-at: 2026-09-04T15:32Z diff --git a/rulings/2026/09/R-20260904-03.md b/rulings/2026/09/R-20260904-03.md index 11195ef..ec88856 100644 --- a/rulings/2026/09/R-20260904-03.md +++ b/rulings/2026/09/R-20260904-03.md @@ -1,7 +1,7 @@ -# R-20260904-03 — drop inference_programme phase 17 +# R-20260904-03 — drop inference_programme cluster_gradient_search_benchmark Project: inference_programme -Phase: phases/inference_programme/cluster_gradient_search_benchmark.md +Task: tasks/inference_programme/cluster_gradient_search_benchmark.md Runs: Ruling: drop Reviewed-at: 2026-09-04T15:32Z diff --git a/rulings/2026/09/R-20260904-04.md b/rulings/2026/09/R-20260904-04.md index 5c029bd..1094b99 100644 --- a/rulings/2026/09/R-20260904-04.md +++ b/rulings/2026/09/R-20260904-04.md @@ -1,7 +1,7 @@ -# R-20260904-04 — drop inference_programme phase 20 +# R-20260904-04 — drop inference_programme mass_pix_gradient_cost_probe Project: inference_programme -Phase: phases/inference_programme/mass_pix_gradient_cost_probe.md +Task: tasks/inference_programme/mass_pix_gradient_cost_probe.md Runs: Ruling: drop Reviewed-at: 2026-09-04T16:01Z diff --git a/rulings/2026/09/R-20260904-05.md b/rulings/2026/09/R-20260904-05.md index 355bd56..00eef89 100644 --- a/rulings/2026/09/R-20260904-05.md +++ b/rulings/2026/09/R-20260904-05.md @@ -1,7 +1,7 @@ -# R-20260904-05 — drop inference_programme phase 18 +# R-20260904-05 — drop inference_programme multiband_compile_census_completion Project: inference_programme -Phase: phases/inference_programme/multiband_compile_census_completion.md +Task: tasks/inference_programme/multiband_compile_census_completion.md Runs: Ruling: drop Reviewed-at: 2026-09-04T16:31Z diff --git a/rulings/2026/09/R-20260907-01.md b/rulings/2026/09/R-20260907-01.md index b3c94f1..2acf987 100644 --- a/rulings/2026/09/R-20260907-01.md +++ b/rulings/2026/09/R-20260907-01.md @@ -1,7 +1,7 @@ -# R-20260907-01 — accept subhalo_validation phase 1 +# R-20260907-01 — accept subhalo_validation delaunay_adapt_split_pl_sersic_0 Project: subhalo_validation -Phase: phases/subhalo_validation/delaunay_adapt_split_pl_sersic_0.md +Task: tasks/subhalo_validation/delaunay_adapt_split_pl_sersic_0.md Runs: 342027, 342231, 342234, 342273 Ruling: accept Reviewed-at: 2026-09-07T16:33Z diff --git a/rulings/AGENTS.md b/rulings/AGENTS.md index 863488b..a89dfaa 100644 --- a/rulings/AGENTS.md +++ b/rulings/AGENTS.md @@ -1,6 +1,6 @@ # Rulings — the ledger of record -`rulings/<YYYY>/<MM>/R-<YYYYMMDD>-<nn>.md`, one file per verdict per phase. +`rulings/<YYYY>/<MM>/R-<YYYYMMDD>-<nn>.md`, one file per verdict per task. This folder is canonical: **a verdict recorded only outside the Cortex does not exist.** A project's own ledger (`DECISIONS.md`, `state.md`, `RESULTS.md`) is scientific commentary — evidence, reasoning, consequences — @@ -16,13 +16,13 @@ them was never written at all. under `rulings/**` as code, which leaves the branch for a human; there is no auto-merge path for an edited ruling. 2. **Supersede, never edit.** A wrong ruling gets a new ruling with - `Supersedes: <old id>` — same project, same phase, later id. The new one - becomes the phase's `Ruling:`. Chains, not trees: supersede the current + `Supersedes: <old id>` — same project, same task, later id. The new one + becomes the task's `Ruling:`. Chains, not trees: supersede the current head, never a ruling that already has a successor. 3. **`cortex.py rule` writes them.** It assigns the id, refuses to touch an - existing file, updates the phase's `Ruling:` and `State:` in the same - change. Write one by hand only to migrate history (phase 4), and run - `cortex.py check` before pushing. + existing file, updates the task's `Ruling:` and `State:` in the same + change. Write one by hand only to migrate history (phase 4 of the birth + epic), and run `cortex.py check` before pushing. 4. **The human's words, verbatim.** `## Ruling` is what they said; `## Evidence` is where to look. Paraphrase belongs in the project ledger. @@ -35,12 +35,12 @@ summary>`). Two branches that both assign `-03` on the same day collide on the trial-merge check in `ledger_merge.yml`, which is the point of running it there. -## One file per phase +## One file per task -A ruling names exactly one phase (`Phase: phases/<project>/<slug>.md`). A -verdict spanning several phases — a REWIND — is N files with the same -`## Ruling` body, one per phase, joined by one `Batch:` slot; `cortex.py rule ---also <phase>` fans them out. `Batch:` is also how rulings filed from one +A ruling names exactly one task (`Task: tasks/<project>/<slug>.md`). A +verdict spanning several tasks — a REWIND — is N files with the same +`## Ruling` body, one per task, joined by one `Batch:` slot; `cortex.py rule +--also <task>` fans them out. `Batch:` is also how rulings filed from one review slot are found together. The header keys and the chain rules `check` enforces are in diff --git a/scripts/cortex.py b/scripts/cortex.py index 2c31478..688321d 100644 --- a/scripts/cortex.py +++ b/scripts/cortex.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 """cortex.py — the Cortex's lifecycle script: check · gates · rule · move · new · retire. -The run-and-ruling registry (project → phase → runs → rulings) is a tree of +The run-and-ruling registry (project → task → runs → rulings) is a tree of markdown files with light headers, and this script is the one thing that writes their state and the one thing that checks it. Every rule it enforces is written down in REFERENCE.md; the transition table there is `move`'s and the @@ -15,10 +15,10 @@ Usage: python3 scripts/cortex.py check # OK or DRIFT (exit 1) - python3 scripts/cortex.py gates # gated phases and their refs - python3 scripts/cortex.py rule <phase> <verb> --body <file> [...] - python3 scripts/cortex.py move <phase> <state> [...] - python3 scripts/cortex.py new <project> <slug> --phase <n> [...] + python3 scripts/cortex.py gates # gated tasks and their refs + python3 scripts/cortex.py rule <task> <verb> --body <file> [...] + python3 scripts/cortex.py move <task> <state> [...] + python3 scripts/cortex.py new <project> <slug> --summary "<≤10 words>" [...] python3 scripts/cortex.py retire <project> --why "<one line>" # a project's row Exit codes: 0 = done · 1 = drift or a refused edit · 2 = bad arguments. @@ -39,12 +39,12 @@ # --------------------------------------------------------------------------- # # vocabulary (REFERENCE.md) # --------------------------------------------------------------------------- # -PHASE_STATES = ( +TASK_STATES = ( "planned", "gated", "ready", "submitted", "running", "pulled", "awaiting-ruling", "accepted", "rerun", "dropped", ) TERMINAL_STATES = {"dropped"} -NON_TERMINAL_STATES = set(PHASE_STATES) - TERMINAL_STATES +NON_TERMINAL_STATES = set(TASK_STATES) - TERMINAL_STATES #: `submitted..accepted` (and `rerun`) — the states that need a `Witness:`. WITNESS_STATES = {"submitted", "running", "pulled", "awaiting-ruling", "accepted", "rerun"} @@ -57,8 +57,8 @@ LEGACY_RUN_STATES = {"legacy", "legacy_wrong"} RESET_RUN_STATES = {"failed", "timeout", "void"} -#: What `new` writes into `## Where to look` before a phase has an output -#: path. It is honest on a `planned` phase and a hole on any other, because +#: What `new` writes into `## Where to look` before a task has an output +#: path. It is honest on a `planned` task and a hole on any other, because #: the section is now rendered as the "which folder do I open" answer on the #: dashboard's `## By project` view and in `pyauto-brain cortex checkin`. WHERE_PLACEHOLDER = "(the output path, once there is one)" @@ -66,31 +66,41 @@ WHERE_EXEMPT_STATES = {"planned"} RULING_VERBS = ("accept", "rerun", "drop", "leave-to-finish") -#: the head verb ↔ phase state agreement `check` enforces. +#: the head verb ↔ task state agreement `check` enforces. VERB_STATES = { "accept": {"accepted"}, "drop": {"dropped"}, "rerun": {"rerun", "ready", "submitted", "running", "pulled", "awaiting-ruling"}, "leave-to-finish": NON_TERMINAL_STATES, } -#: `rule` writes the phase into this state (None = unchanged). +#: `rule` writes the task into this state (None = unchanged). VERB_TARGET = {"accept": "accepted", "rerun": "rerun", "drop": "dropped", "leave-to-finish": None} # Canonical header key order — `new` writes it and the edit helpers insert a # missing key at its slot so a hand-edited file keeps reading the same. -PHASE_KEYS = ( - "Project", "Phase", "State", "Gates", "Reset", "Witness", "Budget", +TASK_KEYS = ( + "Project", "Summary", "State", "Gates", "Reset", "Witness", "Budget", "Runs", "Ruling", "Review-minutes", "Epic", "Filed", "Migrated-from", ) +#: `Summary:` is the board's line: the QUESTION the task answers, in at most +#: ten words. It is required on every task file and `check` enforces both. +SUMMARY_MAX_WORDS = 10 +#: Header keys that were retired, and what to say instead of the generic +#: `unknown header key`. `Phase:` died on 2026-09-07: science is unordered +#: ideas, the slug is the identity, the number carried nothing. +DEAD_KEYS = { + "Phase": "Phase: is a retired header — a task is identified by its slug, " + "not a number (delete the line; `Task:` on a ruling names the path)", +} #: `Batch:` is optional-historical — the 2026-08/09 rulings cite the batch #: record they were filed from; nothing writes new ones (the slot apparatus #: was retired 2026-09-03). RULING_KEYS = ( - "Project", "Phase", "Runs", "Ruling", "Supersedes", "Batch", "Reviewed-at", + "Project", "Task", "Runs", "Ruling", "Supersedes", "Batch", "Reviewed-at", "Review-minutes-actual", "Follow-ups", "Migrated-from", ) -PHASE_SECTIONS = ("Question", "Witness", "Where to look", "Runs", "Ruling") +TASK_SECTIONS = ("Question", "Witness", "Where to look", "Runs", "Ruling") RULING_SECTIONS = ("Ruling", "Evidence") PROJECT_FIELDS = ("remote", "local_path", "ral_root", "mirror", "sync_cli", @@ -130,7 +140,7 @@ RULING_ID_RE = re.compile(r"^R-(\d{4})(\d{2})(\d{2})-(\d{2})$") RULING_FILE_RE = re.compile(r"^rulings/(\d{4})/(\d{2})/(R-\d{8}-\d{2})\.md$") -PHASE_FILE_RE = re.compile(r"^phases/([a-z][a-z0-9_]*)/([^/]+)\.md$") +TASK_FILE_RE = re.compile(r"^tasks/([a-z][a-z0-9_]*)/([^/]+)\.md$") PROJECT_KEY_RE = re.compile(r"^[a-z][a-z0-9_]*$") SLUG_RE = re.compile(r"^[a-z0-9][a-z0-9_-]*$") DATE_RE = re.compile(r"^\d{4}-\d{2}-\d{2}$") @@ -197,7 +207,7 @@ def parse_header(text: str) -> "tuple[str | None, dict[str, str]]": return title, fields -def edit_header(text: str, updates: "dict[str, str | None]", order=PHASE_KEYS) -> str: +def edit_header(text: str, updates: "dict[str, str | None]", order=TASK_KEYS) -> str: """Return `text` with header keys set (value) or removed (None), in place. Every other byte is preserved: a present key has only its value replaced; @@ -475,7 +485,7 @@ def run_problems(runs: "list[Run]", header_runs: str, state: str, for key in ("after", "resumes"): target = r.cont.get(key) if target is not None and (target not in idents or target == r.ident): - problems.append(f"run {r.ident} {key}: {target} names no other run of this phase") + problems.append(f"run {r.ident} {key}: {target} names no other run of this task") ruled = r.cont.get("ruled") if ruled is not None and ruled not in ruling_ids: problems.append(f"run {r.ident} ruled: {ruled} does not resolve to a ruling file") @@ -507,7 +517,7 @@ def gate_url(ref: str) -> str: # --------------------------------------------------------------------------- # # the tree # --------------------------------------------------------------------------- # -class Phase: +class Task: def __init__(self, root: Path, path: Path): self.path = path self.rel = path.relative_to(root).as_posix() @@ -541,15 +551,15 @@ def _md_files(d: Path): return sorted(p for p in d.rglob("*.md")) if d.is_dir() else [] -def load_phases(root: Path) -> "tuple[list[Phase], list[str]]": - phases, problems = [], [] - for p in _md_files(root / "phases"): +def load_tasks(root: Path) -> "tuple[list[Task], list[str]]": + tasks, problems = [], [] + for p in _md_files(root / "tasks"): rel = p.relative_to(root).as_posix() - if not PHASE_FILE_RE.match(rel): - problems.append(f"{rel}: not a phase path (phases/<project>/<slug>.md)") + if not TASK_FILE_RE.match(rel): + problems.append(f"{rel}: not a task path (tasks/<project>/<slug>.md)") continue - phases.append(Phase(root, p)) - return phases, problems + tasks.append(Task(root, p)) + return tasks, problems def load_rulings(root: Path) -> "tuple[list[Ruling], list[str]]": @@ -592,92 +602,92 @@ def batch_reviews(root: Path) -> "list[Path]": # --------------------------------------------------------------------------- # # check # --------------------------------------------------------------------------- # -def phase_problems(root: Path, phases: "list[Phase]", projects: "dict[str, dict]", - rulings: "dict[str, Ruling]", successors: "dict[str, list[str]]") -> "list[str]": +def task_problems(root: Path, tasks: "list[Task]", projects: "dict[str, dict]", + rulings: "dict[str, Ruling]", successors: "dict[str, list[str]]") -> "list[str]": problems: "list[str]" = [] - numbers: "dict[tuple[str, str], str]" = {} - for ph in phases: - f = ph.fields - p = [f"{ph.rel}: {msg}" for msg in ph.run_parse_problems] - if ph.title is None: - p.append(f"{ph.rel}: line 1 is not `# <title>`") - for key in ("Project", "Phase", "State"): + for tk in tasks: + f = tk.fields + p = [f"{tk.rel}: {msg}" for msg in tk.run_parse_problems] + if tk.title is None: + p.append(f"{tk.rel}: line 1 is not `# <title>`") + for key in ("Project", "Summary", "State"): if not f.get(key): - p.append(f"{ph.rel}: missing header key {key}:") + p.append(f"{tk.rel}: missing header key {key}:") for key in f: - if key not in PHASE_KEYS: - p.append(f"{ph.rel}: unknown header key {key}:") - for name in PHASE_SECTIONS: - if name not in sections(ph.text): - p.append(f"{ph.rel}: missing section ## {name}") + if key in DEAD_KEYS: + p.append(f"{tk.rel}: {DEAD_KEYS[key]}") + elif key not in TASK_KEYS: + p.append(f"{tk.rel}: unknown header key {key}:") + summary = f.get("Summary", "") + if summary: + words = summary.split() + if len(words) > SUMMARY_MAX_WORDS: + p.append(f"{tk.rel}: Summary: {len(words)} words — at most " + f"{SUMMARY_MAX_WORDS} (the question the task answers)") + for name in TASK_SECTIONS: + if name not in sections(tk.text): + p.append(f"{tk.rel}: missing section ## {name}") project = f.get("Project", "") - if project and project != ph.project_dir: - p.append(f"{ph.rel}: Project: {project} is not the directory name {ph.project_dir}") + if project and project != tk.project_dir: + p.append(f"{tk.rel}: Project: {project} is not the directory name {tk.project_dir}") if project and project not in projects: - p.append(f"{ph.rel}: Project: {project} is not a projects.yaml key") - number = f.get("Phase", "") - if number and not INT_RE.match(number): - p.append(f"{ph.rel}: Phase: '{number}' is not an integer") - elif number: - prior = numbers.setdefault((ph.project_dir, number), ph.rel) - if prior != ph.rel: - p.append(f"{ph.rel}: duplicate Phase: {number} (also {prior})") + p.append(f"{tk.rel}: Project: {project} is not a projects.yaml key") state = f.get("State", "") - if state and state not in PHASE_STATES: - p.append(f"{ph.rel}: State: '{state}' is not a phase state") + if state and state not in TASK_STATES: + p.append(f"{tk.rel}: State: '{state}' is not a task state") refs, bad = gate_refs(f.get("Gates", "")) for token in bad: - p.append(f"{ph.rel}: Gates: unrecognised ref '{token}' — Repo#N or an " + p.append(f"{tk.rel}: Gates: unrecognised ref '{token}' — Repo#N or an " f"issue/PR URL; no owner/Repo#N form") if f.get("Filed") and not DATE_RE.match(f["Filed"]): - p.append(f"{ph.rel}: Filed: '{f['Filed']}' is not YYYY-MM-DD") + p.append(f"{tk.rel}: Filed: '{f['Filed']}' is not YYYY-MM-DD") if f.get("Budget") and not WALL_RE.match(f["Budget"]): - p.append(f"{ph.rel}: Budget: '{f['Budget']}' is not H+:MM") + p.append(f"{tk.rel}: Budget: '{f['Budget']}' is not H+:MM") if f.get("Review-minutes") and not INT_RE.match(f["Review-minutes"]): - p.append(f"{ph.rel}: Review-minutes: '{f['Review-minutes']}' is not an integer") + p.append(f"{tk.rel}: Review-minutes: '{f['Review-minutes']}' is not an integer") # the offline invariants on top of the table if state == "gated" and not refs: - p.append(f"{ph.rel}: State: gated with an empty Gates:") + p.append(f"{tk.rel}: State: gated with an empty Gates:") if state in WITNESS_STATES and not f.get("Witness"): - p.append(f"{ph.rel}: State: {state} needs a Witness:") + p.append(f"{tk.rel}: State: {state} needs a Witness:") ruling_id = f.get("Ruling", "") if state in RULED_STATES and not ruling_id: - p.append(f"{ph.rel}: State: {state} needs a Ruling: (reachable only through rule)") + p.append(f"{tk.rel}: State: {state} needs a Ruling: (reachable only through rule)") head = None if ruling_id: if not RULING_ID_RE.match(ruling_id): - p.append(f"{ph.rel}: Ruling: '{ruling_id}' is not a ruling id") + p.append(f"{tk.rel}: Ruling: '{ruling_id}' is not a ruling id") elif ruling_id not in rulings: - p.append(f"{ph.rel}: Ruling: {ruling_id} does not resolve to a ruling file") + p.append(f"{tk.rel}: Ruling: {ruling_id} does not resolve to a ruling file") else: head = rulings[ruling_id] if successors.get(ruling_id): - p.append(f"{ph.rel}: Ruling: {ruling_id} is not a chain head " + p.append(f"{tk.rel}: Ruling: {ruling_id} is not a chain head " f"(superseded by {', '.join(successors[ruling_id])})") - if head.get("Phase") != ph.rel: - p.append(f"{ph.rel}: Ruling: {ruling_id} names {head.get('Phase') or '(nothing)'}, not this phase") + if head.get("Task") != tk.rel: + p.append(f"{tk.rel}: Ruling: {ruling_id} names {head.get('Task') or '(nothing)'}, not this task") verb = head.get("Ruling") if state and verb in VERB_STATES and state not in VERB_STATES[verb]: - p.append(f"{ph.rel}: Ruling: {ruling_id} verb '{verb}' does not fit State: {state}") + p.append(f"{tk.rel}: Ruling: {ruling_id} verb '{verb}' does not fit State: {state}") # `## Where to look` is rendered, not just parsed: the by-project view # prints these bullets verbatim as the folders to open, and `collect` - # resolves them to the artefacts it scores. A phase that has left + # resolves them to the artefacts it scores. A task that has left # `planned` with only the template placeholder names nowhere. if state and state not in WHERE_EXEMPT_STATES: - if not [b for b in _where_to_look(ph) if WHERE_PLACEHOLDER not in b]: - p.append(f"{ph.rel}: ## Where to look names nowhere — a phase " + if not [b for b in _where_to_look(tk) if WHERE_PLACEHOLDER not in b]: + p.append(f"{tk.rel}: ## Where to look names nowhere — a task " f"past planned needs at least one bullet that is not " f"'{WHERE_PLACEHOLDER}'") - if state == "pulled" and any(r.state in LIVE_RUN_STATES for r in ph.runs): + if state == "pulled" and any(r.state in LIVE_RUN_STATES for r in tk.runs): if head is None or head.get("Ruling") != "leave-to-finish": - p.append(f"{ph.rel}: State: pulled with a live run needs a leave-to-finish ruling head (--partial)") - p.extend(f"{ph.rel}: {msg}" for msg in - run_problems(ph.runs, f.get("Runs", ""), state, set(rulings))) + p.append(f"{tk.rel}: State: pulled with a live run needs a leave-to-finish ruling head (--partial)") + p.extend(f"{tk.rel}: {msg}" for msg in + run_problems(tk.runs, f.get("Runs", ""), state, set(rulings))) problems.extend(p) return problems -def ruling_problems(root: Path, rulings: "list[Ruling]", phases_by_rel: "dict[str, Phase]", +def ruling_problems(root: Path, rulings: "list[Ruling]", tasks_by_rel: "dict[str, Task]", by_id: "dict[str, Ruling]", successors: "dict[str, list[str]]") -> "list[str]": problems: "list[str]" = [] seen: "dict[str, str]" = {} @@ -688,11 +698,13 @@ def ruling_problems(root: Path, rulings: "list[Ruling]", phases_by_rel: "dict[st p.append(f"{r.rel}: duplicate ruling id {r.id} (also {prior})") if r.title is None or not (r.title == r.id or r.title.startswith(f"{r.id} — ")): p.append(f"{r.rel}: title must be `# {r.id}` or `# {r.id} — <summary>`") - for key in ("Project", "Phase", "Ruling"): + for key in ("Project", "Task", "Ruling"): if not r.get(key): p.append(f"{r.rel}: missing header key {key}:") for key in r.fields: - if key not in RULING_KEYS: + if key in DEAD_KEYS: + p.append(f"{r.rel}: {DEAD_KEYS[key]}") + elif key not in RULING_KEYS: p.append(f"{r.rel}: unknown header key {key}:") for name in RULING_SECTIONS: if name not in sections(r.text): @@ -700,17 +712,17 @@ def ruling_problems(root: Path, rulings: "list[Ruling]", phases_by_rel: "dict[st verb = r.get("Ruling") if verb and verb not in RULING_VERBS: p.append(f"{r.rel}: Ruling: '{verb}' is not a ruling verb") - phase_rel = r.get("Phase") - ph = phases_by_rel.get(phase_rel) - if phase_rel and ph is None: - p.append(f"{r.rel}: Phase: {phase_rel} does not resolve to a phase file") - if ph is not None and r.get("Project") and ph.get("Project") != r.get("Project"): - p.append(f"{r.rel}: Phase: {phase_rel} is not a phase of Project: {r.get('Project')}") - if ph is not None: + task_rel = r.get("Task") + tk = tasks_by_rel.get(task_rel) + if task_rel and tk is None: + p.append(f"{r.rel}: Task: {task_rel} does not resolve to a task file") + if tk is not None and r.get("Project") and tk.get("Project") != r.get("Project"): + p.append(f"{r.rel}: Task: {task_rel} is not a task of Project: {r.get('Project')}") + if tk is not None: stems = {s.strip() for s in r.get("Runs").split(",") if s.strip()} - phase_stems = {x.stem for x in ph.runs} - if not stems <= phase_stems: - p.append(f"{r.rel}: Runs: {{{', '.join(sorted(stems - phase_stems))}}} not in the phase's runs") + task_stems = {x.stem for x in tk.runs} + if not stems <= task_stems: + p.append(f"{r.rel}: Runs: {{{', '.join(sorted(stems - task_stems))}}} not in the task's runs") sup = r.get("Supersedes") if sup: if sup == r.id: @@ -721,8 +733,8 @@ def ruling_problems(root: Path, rulings: "list[Ruling]", phases_by_rel: "dict[st old = by_id[sup] if not sup < r.id: p.append(f"{r.rel}: Supersedes: {sup} is not earlier than {r.id}") - if (old.get("Project"), old.get("Phase")) != (r.get("Project"), r.get("Phase")): - p.append(f"{r.rel}: Supersedes: {sup} names a different project/phase") + if (old.get("Project"), old.get("Task")) != (r.get("Project"), r.get("Task")): + p.append(f"{r.rel}: Supersedes: {sup} names a different project/task") if len(successors.get(r.id, [])) > 1: p.append(f"{r.rel}: has {len(successors[r.id])} successors " f"({', '.join(successors[r.id])}) — a chain, not a tree") @@ -743,7 +755,7 @@ def ruling_problems(root: Path, rulings: "list[Ruling]", phases_by_rel: "dict[st def check_problems(root: Path) -> "list[str]": """Every `check` rule, over the tree at `root`. Hermetic.""" projects, problems = load_projects(root) - phases, stray = load_phases(root) + tasks, stray = load_tasks(root) problems.extend(stray) rulings, stray = load_rulings(root) problems.extend(stray) @@ -754,9 +766,9 @@ def check_problems(root: Path) -> "list[str]": for r in rulings: if r.get("Supersedes"): successors.setdefault(r.get("Supersedes"), []).append(r.id) - phases_by_rel = {ph.rel: ph for ph in phases} - problems.extend(phase_problems(root, phases, projects, by_id, successors)) - problems.extend(ruling_problems(root, rulings, phases_by_rel, by_id, successors)) + tasks_by_rel = {tk.rel: tk for tk in tasks} + problems.extend(task_problems(root, tasks, projects, by_id, successors)) + problems.extend(ruling_problems(root, rulings, tasks_by_rel, by_id, successors)) return problems @@ -775,22 +787,22 @@ def cmd_check(args) -> int: # gates # --------------------------------------------------------------------------- # def gates_report(root: Path) -> "tuple[list[str], int]": - """(report lines, exit code) — every `gated` phase, its refs and their URLs. + """(report lines, exit code) — every `gated` task, its refs and their URLs. Read-only and offline: nothing asks GitHub whether a ref has cleared. Gate grading was retired 2026-09-03 — 2 gated refs and 0 flips in its whole life, while schema decision 54 routes sequencing through prose - `Ready when:` lines. A gated phase is moved on by a human reading this - listing and typing `move <phase> ready`.""" - phases, _ = load_phases(root) - wanted = [ph for ph in phases - if ph.state == "gated" and gate_refs(ph.get("Gates"))[0]] + `Ready when:` lines. A gated task is moved on by a human reading this + listing and typing `move <task> ready`.""" + tasks, _ = load_tasks(root) + wanted = [tk for tk in tasks + if tk.state == "gated" and gate_refs(tk.get("Gates"))[0]] if not wanted: - return ["gates: no gated phase"], 0 - lines = [f"gates: {len(wanted)} phase(s)"] - for ph in wanted: - refs = gate_refs(ph.get("Gates"))[0] - lines.append(f" {ph.rel}: {ph.state} — {', '.join(refs)}") + return ["gates: no gated task"], 0 + lines = [f"gates: {len(wanted)} task(s)"] + for tk in wanted: + refs = gate_refs(tk.get("Gates"))[0] + lines.append(f" {tk.rel}: {tk.state} — {', '.join(refs)}") for ref in refs: lines.append(f" {ref} → {gate_url(ref)}") return lines, 0 @@ -814,21 +826,21 @@ def _today(args) -> date: return date.today() -def _phase_at(root: Path, ref: str) -> Phase: - """The phase file `ref` names — repo-relative, or a path under root.""" +def _task_at(root: Path, ref: str) -> Task: + """The task file `ref` names — repo-relative, or a path under root.""" p = Path(ref) path = p if p.is_absolute() else root / p if not path.is_file(): - raise CortexError(f"no phase file at {ref}") + raise CortexError(f"no task file at {ref}") try: path = path.resolve() path.relative_to(root.resolve()) except ValueError: raise CortexError(f"{ref} is outside {root}") rel_path = root.resolve() / path.relative_to(root.resolve()) - if not PHASE_FILE_RE.match(path.relative_to(root.resolve()).as_posix()): - raise CortexError(f"{ref} is not phases/<project>/<slug>.md") - return Phase(root.resolve(), rel_path) + if not TASK_FILE_RE.match(path.relative_to(root.resolve()).as_posix()): + raise CortexError(f"{ref} is not tasks/<project>/<slug>.md") + return Task(root.resolve(), rel_path) def _partition_for(root: Path, project: str, given: "str | None") -> str: @@ -852,36 +864,36 @@ def _run_line(ident: str, state: str, partition: str, day: date, wall: str = "0: return f"{line} — {note}" if note else line -def _append_run(ph: Phase, ident: str, state: str, partition: str, day: date, *, +def _append_run(tk: Task, ident: str, state: str, partition: str, day: date, *, note: str = "", cont: "dict[str, str] | None" = None) -> str: - """Phase text with one run line (and its continuations) appended to + """Task text with one run line (and its continuations) appended to `## Runs` and the `Runs:` header re-derived from the body.""" if not RUN_IDENT_RE.match(ident): raise CortexError(f"--run '{ident}' is not <stem>[_<task>|_[<set>]]") probe = Run(RUN_LINE_RE.match(_run_line(ident, state, partition, day)), 0) - for r in ph.runs: + for r in tk.runs: if r.stem == probe.stem and _overlaps(r.task_set(), probe.task_set()): - raise CortexError(f"run {ident} overlaps {r.ident} already on this phase") - idents = {r.ident for r in ph.runs} + raise CortexError(f"run {ident} overlaps {r.ident} already on this task") + idents = {r.ident for r in tk.runs} for key, value in (cont or {}).items(): if key in ("after", "resumes") and value not in idents: - raise CortexError(f"{key}: {value} names no run of this phase") + raise CortexError(f"{key}: {value} names no run of this task") new_lines = [_run_line(ident, state, partition, day, note=note)] new_lines += [f" {k}: {v}" for k, v in (cont or {}).items()] - text = append_to_section(ph.text, "Runs", new_lines) + text = append_to_section(tk.text, "Runs", new_lines) stems = [] - for r in ph.runs + [probe]: + for r in tk.runs + [probe]: if r.stem not in stems: stems.append(r.stem) return edit_header(text, {"Runs": ", ".join(stems)}) -def _add_pulled_to(ph: Phase, text: str, pulled_to: "str | None", states: "set[str]") -> str: +def _add_pulled_to(tk: Task, text: str, pulled_to: "str | None", states: "set[str]") -> str: """Give every run in `states` a `pulled_to:` it lacks — `pulled_to` if given, else the run's own `where:` (a legacy run's quarantine path is where its results already are). Refuse when no run would carry one, so `move pulled` never writes a state `check` rejects.""" - targets = [r for r in ph.runs if r.state in states] + targets = [r for r in tk.runs if r.state in states] if not targets: raise CortexError(f"no {' | '.join(sorted(states))} run to pull — nothing to review " "(rule drop, or fix the run lines)") @@ -905,32 +917,32 @@ def _add_pulled_to(ph: Phase, text: str, pulled_to: "str | None", states: "set[s # --------------------------------------------------------------------------- # # move # --------------------------------------------------------------------------- # -def move_phase(root: Path, ref: str, to: str, *, run: "str | None" = None, - reason: "str | None" = None, - partial: bool = False, partition: "str | None" = None, - after: "str | None" = None, resumes: "str | None" = None, - note: str = "", pulled_to: "str | None" = None, - today: "date | None" = None) -> str: +def move_task(root: Path, ref: str, to: str, *, run: "str | None" = None, + reason: "str | None" = None, + partial: bool = False, partition: "str | None" = None, + after: "str | None" = None, resumes: "str | None" = None, + note: str = "", pulled_to: "str | None" = None, + today: "date | None" = None) -> str: """Apply one edge of the transition table; return a one-line summary.""" today = today or date.today() - ph = _phase_at(root, ref) - cur = ph.state - if cur not in PHASE_STATES: - raise CortexError(f"{ph.rel} has State: '{cur}', which is not a phase state") - if to not in PHASE_STATES: - raise CortexError(f"'{to}' is not a phase state ({' | '.join(PHASE_STATES)})") + tk = _task_at(root, ref) + cur = tk.state + if cur not in TASK_STATES: + raise CortexError(f"{tk.rel} has State: '{cur}', which is not a task state") + if to not in TASK_STATES: + raise CortexError(f"'{to}' is not a task state ({' | '.join(TASK_STATES)})") if cur == "dropped": - raise CortexError(f"{ph.rel} is dropped — terminal; revival is a new phase number") + raise CortexError(f"{tk.rel} is dropped — terminal; revival is a new slug") if to == "dropped": - raise CortexError("dropped is a ruling edge: `cortex.py rule <phase> drop`") + raise CortexError("dropped is a ruling edge: `cortex.py rule <task> drop`") if to in ("accepted", "rerun"): - raise CortexError(f"{to} is a ruling edge: `cortex.py rule <phase> {VERB_FOR_STATE[to]}`" + raise CortexError(f"{to} is a ruling edge: `cortex.py rule <task> {VERB_FOR_STATE[to]}`" + (" --supersedes <Ruling:>" if cur == "accepted" else "")) - refs, bad = gate_refs(ph.get("Gates")) + refs, bad = gate_refs(tk.get("Gates")) if bad: - raise CortexError(f"{ph.rel}: Gates: unrecognised ref '{bad[0]}' — fix it first") + raise CortexError(f"{tk.rel}: Gates: unrecognised ref '{bad[0]}' — fix it first") updates: "dict[str, str | None]" = {} - text = ph.text + text = tk.text cont = {} if after: cont["after"] = after @@ -939,75 +951,75 @@ def move_phase(root: Path, ref: str, to: str, *, run: "str | None" = None, if cur == to: if cur in ("submitted", "running") and run: - part = _partition_for(root, ph.project_dir, partition) - text = _append_run(ph, run, "submitted", part, today, note=note, cont=cont) - ph.path.write_text(text, encoding="utf-8") - return f"{ph.rel}: {cur} — appended run {run}" - raise CortexError(f"{ph.rel} is already {cur}" + part = _partition_for(root, tk.project_dir, partition) + text = _append_run(tk, run, "submitted", part, today, note=note, cont=cont) + tk.path.write_text(text, encoding="utf-8") + return f"{tk.rel}: {cur} — appended run {run}" + raise CortexError(f"{tk.rel} is already {cur}" + ("" if cur in ("submitted", "running") else - "; --run is for submitted | running phases")) + "; --run is for submitted | running tasks")) edge = (cur, to) if run and edge not in (("ready", "submitted"), ("submitted", "running")): - raise CortexError("--run applies to ready → submitted and to a submitted | running phase") + raise CortexError("--run applies to ready → submitted and to a submitted | running task") if edge == ("planned", "gated"): if not refs: - raise CortexError(f"{ph.rel}: Gates: is empty — planned → ready") + raise CortexError(f"{tk.rel}: Gates: is empty — planned → ready") elif edge == ("planned", "ready"): if refs: - raise CortexError(f"{ph.rel}: Gates: is non-empty — planned → gated") + raise CortexError(f"{tk.rel}: Gates: is non-empty — planned → gated") elif edge == ("gated", "ready"): pass # the human read `gates` and judged the refs cleared elif edge == ("ready", "gated"): raise CortexError("ready → gated is a hand edit of the header — re-gating a " - "ready phase is a judgement, not an edge") + "ready task is a judgement, not an edge") elif edge == ("ready", "submitted"): - if not ph.get("Witness"): - raise CortexError(f"{ph.rel}: Witness: is empty — register the witness before submitting") + if not tk.get("Witness"): + raise CortexError(f"{tk.rel}: Witness: is empty — register the witness before submitting") if not run: raise CortexError("ready → submitted needs --run <id>") - part = _partition_for(root, ph.project_dir, partition) - text = _append_run(ph, run, "submitted", part, today, note=note, cont=cont) + part = _partition_for(root, tk.project_dir, partition) + text = _append_run(tk, run, "submitted", part, today, note=note, cont=cont) elif edge == ("ready", "pulled"): - if not ph.runs or not all(r.state in LEGACY_RUN_STATES for r in ph.runs): - raise CortexError("ready → pulled is for a legacy-born phase: every run line " + if not tk.runs or not all(r.state in LEGACY_RUN_STATES for r in tk.runs): + raise CortexError("ready → pulled is for a legacy-born task: every run line " "legacy | legacy_wrong") - if not ph.get("Witness"): - raise CortexError(f"{ph.rel}: Witness: is empty — still mandatory for a legacy-born phase") - text = _add_pulled_to(ph, text, pulled_to, {"legacy"}) + if not tk.get("Witness"): + raise CortexError(f"{tk.rel}: Witness: is empty — still mandatory for a legacy-born task") + text = _add_pulled_to(tk, text, pulled_to, {"legacy"}) elif edge == ("submitted", "running"): if run: - part = _partition_for(root, ph.project_dir, partition) - text = _append_run(ph, run, "submitted", part, today, note=note, cont=cont) + part = _partition_for(root, tk.project_dir, partition) + text = _append_run(tk, run, "submitted", part, today, note=note, cont=cont) elif edge in (("submitted", "ready"), ("running", "ready")): - if any(r.state in LIVE_RUN_STATES for r in ph.runs): - raise CortexError(f"{ph.rel}: a run is still submitted | running") - if not any(r.state in RESET_RUN_STATES for r in ph.runs): - raise CortexError(f"{ph.rel}: no failed | timeout | void run to reset from") + if any(r.state in LIVE_RUN_STATES for r in tk.runs): + raise CortexError(f"{tk.rel}: a run is still submitted | running") + if not any(r.state in RESET_RUN_STATES for r in tk.runs): + raise CortexError(f"{tk.rel}: no failed | timeout | void run to reset from") if not reason: raise CortexError(f"{cur} → ready needs --reason") updates["Reset"] = reason elif edge == ("running", "pulled"): - if any(r.state in LIVE_RUN_STATES for r in ph.runs) and not partial: - raise CortexError(f"{ph.rel}: a run is still submitted | running — " + if any(r.state in LIVE_RUN_STATES for r in tk.runs) and not partial: + raise CortexError(f"{tk.rel}: a run is still submitted | running — " "--partial for a partial array (then rule leave-to-finish)") - text = _add_pulled_to(ph, text, pulled_to, {"done"}) + text = _add_pulled_to(tk, text, pulled_to, {"done"}) elif edge in (("pulled", "awaiting-ruling"), ("rerun", "ready")): pass else: raise CortexError(f"no edge {cur} → {to} in the transition table") updates["State"] = to - ph.path.write_text(edit_header(text, updates), encoding="utf-8") - return f"{ph.rel}: {cur} → {to}" + tk.path.write_text(edit_header(text, updates), encoding="utf-8") + return f"{tk.rel}: {cur} → {to}" VERB_FOR_STATE = {"accepted": "accept", "rerun": "rerun", "dropped": "drop"} def cmd_move(args) -> int: - print(move_phase(args.root, args.phase, args.state, run=args.run, reason=args.reason, - partial=args.partial, partition=args.partition, - after=args.after, resumes=args.resumes, note=args.note or "", - pulled_to=args.pulled_to, today=_today(args))) + print(move_task(args.root, args.task, args.state, run=args.run, reason=args.reason, + partial=args.partial, partition=args.partition, + after=args.after, resumes=args.resumes, note=args.note or "", + pulled_to=args.pulled_to, today=_today(args))) return 0 @@ -1020,7 +1032,7 @@ def retire_project(root: Path, key: str, why: str, today: date) -> str: Three things it deliberately does not do. It does not **delete the row**: that row is the only record of where the project's data lives, and a - retired project still has to be findable. It does not touch a **phase** or + retired project still has to be findable. It does not touch a **task** or a **ruling**: `rulings/` is append-only and history is not rewritten by a change of status. And it does not retire over **live work** — every state outside `RULED_STATES` and `planned` is an unfinished question, so the @@ -1046,10 +1058,10 @@ def retire_project(root: Path, key: str, why: str, today: date) -> str: raise CortexError(f"{key} is not a projects.yaml key") if projects[key].get("status") == "retired": raise CortexError(f"{key} is already retired") - phases, _ = load_phases(root) - live = [f"{ph.rel} — {ph.state}" for ph in phases - if ph.project_dir == key - and ph.state not in RULED_STATES | {"planned"}] + tasks, _ = load_tasks(root) + live = [f"{tk.rel} — {tk.state}" for tk in tasks + if tk.project_dir == key + and tk.state not in RULED_STATES | {"planned"}] if live: raise CortexError(f"{key} still has live work — rule or drop it " f"first: {', '.join(live)}") @@ -1107,28 +1119,28 @@ def next_ruling_id(root: Path, day: date, taken: "set[str] | None" = None) -> st raise CortexError(f"99 rulings already filed on {day.isoformat()}") -def _ruling_target(ph: Phase, verb: str, supersedes: "str | None", by_id: "dict[str, Ruling]", +def _ruling_target(tk: Task, verb: str, supersedes: "str | None", by_id: "dict[str, Ruling]", successors: "dict[str, list[str]]") -> "str | None": - """The phase state `verb` writes, or raise if the table forbids it.""" - state = ph.state + """The task state `verb` writes, or raise if the table forbids it.""" + state = tk.state if verb not in RULING_VERBS: raise CortexError(f"'{verb}' is not a ruling verb ({' | '.join(RULING_VERBS)})") if state == "dropped": - raise CortexError(f"{ph.rel} is dropped — terminal") + raise CortexError(f"{tk.rel} is dropped — terminal") if supersedes: if supersedes not in by_id: raise CortexError(f"--supersedes {supersedes} does not resolve to a ruling file") old = by_id[supersedes] - if old.get("Phase") != ph.rel: - raise CortexError(f"--supersedes {supersedes} rules on {old.get('Phase')}, not {ph.rel}") + if old.get("Task") != tk.rel: + raise CortexError(f"--supersedes {supersedes} rules on {old.get('Task')}, not {tk.rel}") if successors.get(supersedes): raise CortexError(f"{supersedes} already has a successor " f"({successors[supersedes][0]}) — supersede the head") if state == "accepted": if verb not in ("rerun", "drop"): - raise CortexError(f"accepted takes only rerun | drop, with --supersedes {ph.get('Ruling')}") - if supersedes != ph.get("Ruling"): - raise CortexError(f"accepted → {VERB_TARGET[verb]} needs --supersedes {ph.get('Ruling')} " + raise CortexError(f"accepted takes only rerun | drop, with --supersedes {tk.get('Ruling')}") + if supersedes != tk.get("Ruling"): + raise CortexError(f"accepted → {VERB_TARGET[verb]} needs --supersedes {tk.get('Ruling')} " "(the REWIND case)") return VERB_TARGET[verb] if verb == "leave-to-finish": @@ -1144,11 +1156,11 @@ def _ruling_target(ph: Phase, verb: str, supersedes: "str | None", by_id: "dict[ f"(the table: awaiting-ruling → {VERB_TARGET[verb]})") -def rule_phase(root: Path, ref: str, verb: str, body: str, *, supersedes: "str | None" = None, - batch: "str | None" = None, minutes: "int | None" = None, - follow_ups: "tuple[str, ...]" = (), - today: "date | None" = None, now: "datetime | None" = None) -> "list[str]": - """File the ruling for one phase and update its `Ruling:` and `State:`; +def rule_task(root: Path, ref: str, verb: str, body: str, *, supersedes: "str | None" = None, + batch: "str | None" = None, minutes: "int | None" = None, + follow_ups: "tuple[str, ...]" = (), + today: "date | None" = None, now: "datetime | None" = None) -> "list[str]": + """File the ruling for one task and update its `Ruling:` and `State:`; return the ruling paths written (a list of one). Everything is validated before anything is written.""" today = today or date.today() @@ -1170,12 +1182,12 @@ def rule_phase(root: Path, ref: str, verb: str, body: str, *, supersedes: "str | if not body.strip(): raise CortexError("--body is empty — the human's words, verbatim") - ph = _phase_at(root, ref) - plan = [(ph, supersedes, _ruling_target(ph, verb, supersedes, by_id, successors))] + tk = _task_at(root, ref) + plan = [(tk, supersedes, _ruling_target(tk, verb, supersedes, by_id, successors))] taken: "set[str]" = set() written = [] - for ph, sup, new_state in plan: + for tk, sup, new_state in plan: rid = next_ruling_id(root, today, taken) taken.add(rid) if sup and not sup < rid: @@ -1183,14 +1195,14 @@ def rule_phase(root: Path, ref: str, verb: str, body: str, *, supersedes: "str | path = root / "rulings" / rid[2:6] / rid[6:8] / f"{rid}.md" if path.exists(): raise CortexError(f"refusing to touch an existing ruling: {path.relative_to(root)}") - plan_item = (ph, sup, new_state, rid, path) + plan_item = (tk, sup, new_state, rid, path) written.append(plan_item) stamp = f"{today.isoformat()}T{now.strftime('%H:%M')}Z" out = [] - for ph, sup, new_state, rid, path in written: - header = [f"Project: {ph.get('Project')}", f"Phase: {ph.rel}", - f"Runs: {ph.get('Runs')}" if ph.get("Runs") else "Runs:", + for tk, sup, new_state, rid, path in written: + header = [f"Project: {tk.get('Project')}", f"Task: {tk.rel}", + f"Runs: {tk.get('Runs')}" if tk.get("Runs") else "Runs:", f"Ruling: {verb}"] if sup: header.append(f"Supersedes: {sup}") @@ -1201,8 +1213,8 @@ def rule_phase(root: Path, ref: str, verb: str, body: str, *, supersedes: "str | header.append(f"Review-minutes-actual: {minutes}") if follow_ups: header.append(f"Follow-ups: {', '.join(follow_ups)}") - evidence = _where_to_look(ph) or ["- (none given)"] - text = "\n".join([f"# {rid} — {verb} {ph.get('Project')} phase {ph.get('Phase')}", ""] + evidence = _where_to_look(tk) or ["- (none given)"] + text = "\n".join([f"# {rid} — {verb} {tk.get('Project')} {tk.slug}", ""] + header + ["", "## Ruling", "", body, "", "## Evidence", ""] + evidence) + "\n" path.parent.mkdir(parents=True, exist_ok=True) @@ -1211,17 +1223,17 @@ def rule_phase(root: Path, ref: str, verb: str, body: str, *, supersedes: "str | if new_state: updates["State"] = new_state line = f"{rid} — {verb}" + (f" (supersedes {sup})" if sup else "") - ptext = append_to_section(ph.text, "Ruling", [line], replace_placeholder="(none)") - ph.path.write_text(edit_header(ptext, updates), encoding="utf-8") + ptext = append_to_section(tk.text, "Ruling", [line], replace_placeholder="(none)") + tk.path.write_text(edit_header(ptext, updates), encoding="utf-8") out.append(path.relative_to(root).as_posix()) return out -def _where_to_look(ph: Phase) -> "list[str]": - span = sections(ph.text).get("Where to look") +def _where_to_look(tk: Task) -> "list[str]": + span = sections(tk.text).get("Where to look") if span is None: return [] - lines = ph.text.split("\n")[span[0]:span[1]] + lines = tk.text.split("\n")[span[0]:span[1]] return [ln for ln in lines if ln.startswith("- ") and ln.strip() != "-"] @@ -1229,9 +1241,9 @@ def cmd_rule(args) -> int: body_path = Path(args.body) if not body_path.is_file(): raise CortexError(f"--body {args.body} is not a file") - paths = rule_phase(args.root, args.phase, args.verb, body_path.read_text(encoding="utf-8"), - supersedes=args.supersedes, batch=args.batch, minutes=args.minutes, - follow_ups=tuple(args.follow_up or ()), today=_today(args)) + paths = rule_task(args.root, args.task, args.verb, body_path.read_text(encoding="utf-8"), + supersedes=args.supersedes, batch=args.batch, minutes=args.minutes, + follow_ups=tuple(args.follow_up or ()), today=_today(args)) for p in paths: print(f"wrote {p}") return 0 @@ -1240,13 +1252,13 @@ def cmd_rule(args) -> int: # --------------------------------------------------------------------------- # # new # --------------------------------------------------------------------------- # -def new_phase(root: Path, project: str, slug: str, number: int, *, gates: str = "", - epic: str = "", legacy_runs: "tuple[str, ...]" = (), - legacy_wrong: "tuple[str, ...]" = (), where: "str | None" = None, - partition: "str | None" = None, witness: str = "", budget: str = "", - minutes: "int | None" = None, title: "str | None" = None, - today: "date | None" = None) -> str: - """Write phases/<project>/<slug>.md from the template; return its rel path.""" +def new_task(root: Path, project: str, slug: str, summary: str, *, gates: str = "", + epic: str = "", legacy_runs: "tuple[str, ...]" = (), + legacy_wrong: "tuple[str, ...]" = (), where: "str | None" = None, + partition: "str | None" = None, witness: str = "", budget: str = "", + minutes: "int | None" = None, title: "str | None" = None, + today: "date | None" = None) -> str: + """Write tasks/<project>/<slug>.md from the template; return its rel path.""" today = today or date.today() projects, problems = load_projects(root) if project not in projects: @@ -1254,22 +1266,24 @@ def new_phase(root: Path, project: str, slug: str, number: int, *, gates: str = + (f" ({problems[0]})" if problems else "")) if not SLUG_RE.match(slug): raise CortexError(f"slug '{slug}' must match {SLUG_RE.pattern}") - if number < 1: - raise CortexError("--phase must be a positive integer") - path = root / "phases" / project / f"{slug}.md" + summary = " ".join((summary or "").split()) + if not summary: + raise CortexError("--summary must say what the task asks, in one line") + if len(summary.split()) > SUMMARY_MAX_WORDS: + raise CortexError(f"--summary is {len(summary.split())} words — at most " + f"{SUMMARY_MAX_WORDS} (the question the task answers)") + # The slug IS the identity: the path is unique per project, so there is + # nothing else to collide on. + path = root / "tasks" / project / f"{slug}.md" if path.exists(): raise CortexError(f"{path.relative_to(root).as_posix()} already exists") - for existing in sorted((root / "phases" / project).glob("*.md")) if (root / "phases" / project).is_dir() else []: - _, fields = parse_header(existing.read_text(encoding="utf-8")) - if fields.get("Phase") == str(number): - raise CortexError(f"phase {number} already exists: {existing.relative_to(root).as_posix()}") _, bad = gate_refs(gates) if bad: raise CortexError(f"--gates '{bad[0]}' is not Repo#N or an issue/PR URL (no owner/Repo#N form)") legacy = [(r, "legacy") for r in legacy_runs] + [(r, "legacy_wrong") for r in legacy_wrong] if legacy: if gates.strip(): - raise CortexError("a legacy-born phase cannot be gated — its runs already happened") + raise CortexError("a legacy-born task cannot be gated — its runs already happened") if not where: raise CortexError("--legacy-run needs --where <quarantine path> (check requires where:)") for ident, _ in legacy: @@ -1287,7 +1301,7 @@ def new_phase(root: Path, project: str, slug: str, number: int, *, gates: str = words = slug.replace("_", " ").replace("-", " ") title = title or words header = [ - f"Project: {project}", f"Phase: {number}", f"State: {state}", + f"Project: {project}", f"Summary: {summary}", f"State: {state}", f"Gates: {gates.strip()}" if gates.strip() else "Gates:", f"Witness: {witness}" if witness else "Witness:", f"Budget: {budget}" if budget else "Budget:", @@ -1301,11 +1315,11 @@ def new_phase(root: Path, project: str, slug: str, number: int, *, gates: str = run_lines.append(_run_line(ident, rstate, part, today, note="pre-Cortex run, migrated")) run_lines.append(f" where: {where}") body = [ - f"# {project.capitalize()} — phase {number}: {title}", "", + f"# {project.capitalize()} — {title}", "", *header, "", - "## Question", "", "(the question this phase answers)", "", - "## Witness", "", witness or "(not yet registered — a planned phase may leave this empty)", "", - # A legacy-born phase already knows where to look — `--where` is the + "## Question", "", "(the question this task answers)", "", + "## Witness", "", witness or "(not yet registered — a planned task may leave this empty)", "", + # A legacy-born task already knows where to look — `--where` is the # quarantine path its runs landed in, and it is required for one — so # the section names it rather than the placeholder `check` refuses on # anything past `planned`. @@ -1322,12 +1336,12 @@ def new_phase(root: Path, project: str, slug: str, number: int, *, gates: str = def cmd_new(args) -> int: - rel = new_phase(args.root, args.project, args.slug, args.phase, gates=args.gates or "", - epic=args.epic or "", legacy_runs=tuple(args.legacy_run or ()), - legacy_wrong=tuple(args.legacy_wrong or ()), where=args.where, - partition=args.partition, witness=args.witness or "", - budget=args.budget or "", minutes=args.minutes, title=args.title, - today=_today(args)) + rel = new_task(args.root, args.project, args.slug, args.summary, gates=args.gates or "", + epic=args.epic or "", legacy_runs=tuple(args.legacy_run or ()), + legacy_wrong=tuple(args.legacy_wrong or ()), where=args.where, + partition=args.partition, witness=args.witness or "", + budget=args.budget or "", minutes=args.minutes, title=args.title, + today=_today(args)) print(f"wrote {rel}") return 0 @@ -1352,13 +1366,13 @@ def build_parser() -> argparse.ArgumentParser: _common(c) c.set_defaults(func=cmd_check) - g = sub.add_parser("gates", help="list every gated phase, its refs and their URLs") + g = sub.add_parser("gates", help="list every gated task, its refs and their URLs") _common(g) g.set_defaults(func=cmd_gates) - r = sub.add_parser("rule", help="file a ruling and move the phase per the table") + r = sub.add_parser("rule", help="file a ruling and move the task per the table") _common(r, dated=True) - r.add_argument("phase", help="phases/<project>/<slug>.md") + r.add_argument("task", help="tasks/<project>/<slug>.md") r.add_argument("verb", choices=RULING_VERBS) r.add_argument("--body", required=True, help="file holding the human's words, verbatim") r.add_argument("--supersedes", help="the ruling id this one replaces (the chain head)") @@ -1370,10 +1384,10 @@ def build_parser() -> argparse.ArgumentParser: m = sub.add_parser("move", help="one edge of the transition table") _common(m, dated=True) - m.add_argument("phase", help="phases/<project>/<slug>.md") + m.add_argument("task", help="tasks/<project>/<slug>.md") m.add_argument("state", help="the state to move to") m.add_argument("--run", metavar="ID", help="SLURM job id (<stem>[_<task>|_[<set>]]) to append") - m.add_argument("--reason", help="why a submitted | running phase goes back to ready (Reset:)") + m.add_argument("--reason", help="why a submitted | running task goes back to ready (Reset:)") m.add_argument("--partial", action="store_true", help="running → pulled with a run still live (needs a leave-to-finish ruling)") m.add_argument("--partition", help="the run line's partition (default: the project's row)") @@ -1385,11 +1399,12 @@ def build_parser() -> argparse.ArgumentParser: "run lacking one (default for a legacy run: its where:)") m.set_defaults(func=cmd_move) - n = sub.add_parser("new", help="write phases/<project>/<slug>.md from the template") + n = sub.add_parser("new", help="write tasks/<project>/<slug>.md from the template") _common(n, dated=True) n.add_argument("project") n.add_argument("slug") - n.add_argument("--phase", type=int, required=True, help="the phase number (unique per project)") + n.add_argument("--summary", required=True, + help=f"the question the task answers, at most {SUMMARY_MAX_WORDS} words") n.add_argument("--gates", help="comma-separated Repo#N or issue/PR URLs") n.add_argument("--epic", help="the epic slug shared with the Mind") n.add_argument("--legacy-run", action="append", metavar="ID", help="a pre-Cortex run, reusable") @@ -1399,7 +1414,7 @@ def build_parser() -> argparse.ArgumentParser: n.add_argument("--witness", help="the pre-registered checkable claim") n.add_argument("--budget", help="wall budget per run, H+:MM") n.add_argument("--minutes", type=int, help="Review-minutes seed") - n.add_argument("--title", help="the title after `phase <n>:` (default: the slug's words)") + n.add_argument("--title", help="the title after `# <Project> — ` (default: the slug's words)") n.set_defaults(func=cmd_new) t = sub.add_parser("retire", help="flip a project's row to status: retired") diff --git a/scripts/ledger_merge.py b/scripts/ledger_merge.py index ea07456..49722bb 100755 --- a/scripts/ledger_merge.py +++ b/scripts/ledger_merge.py @@ -3,11 +3,11 @@ WHY THIS EXISTS. The Cortex's own work strands exactly as the Mind's does. A branch-scoped session (the phone, claude.ai/code, any `claude/**` flow) pushes -a phase move, a ruling, a batch record to a feature branch and nothing moves +a task move, a ruling, a batch record to a feature branch and nothing moves it: no workflow looks at a `claude/**` push. The branch sits there until a human writes an explicit "merge this" prompt. -Almost all of what strands is *ledger*: a phase file under `phases/`, a ruling +Almost all of what strands is *ledger*: a task file under `tasks/`, a ruling under `rulings/`, a batch record or review under `batches/`, a check-in stamp. It is the organism's own bookkeeping, it is template-shaped, its drift check (`cortex.py check`) is already automated, and a human reviewing it adds @@ -48,11 +48,11 @@ import sys from pathlib import Path -# Directories holding nothing but the run-and-ruling ledger: phase files, +# Directories holding nothing but the run-and-ruling ledger: task files, # rulings of record, and the batch records and reviews kept as history. Their # whole contents are ledger (subject to the EXCLUDED_NAMES guard below, and to # the append-only leg for rulings/ and batches/ in classify_entries). -LEDGER_DIRS = ("phases/", "rulings/", "batches/") +LEDGER_DIRS = ("tasks/", "rulings/", "batches/") # Root files that are ledger state. Deliberately NOT here: README.md, # AGENTS.md, CLAUDE.md, REFERENCE.md — prose a human reads, changed rarely @@ -62,7 +62,7 @@ # # `dashboard.md` / `dashboard.html` join it as GENERATED ledger: the cortex # conductor renders them from the registry, `dashboard_refresh.yml` self-heals -# them on main, and a branch that moves a phase re-renders them in the same +# them on main, and a branch that moves a task re-renders them in the same # push. If they were code, every ordinary ledger branch would stop for a human # on two files nobody wrote by hand. `checkin.yaml` is the same kind of thing: # one machine-written stamp, pushed by the check-in that earned it. diff --git a/phases/euclid/magnification_robustness.md b/tasks/euclid/magnification_robustness.md similarity index 83% rename from phases/euclid/magnification_robustness.md rename to tasks/euclid/magnification_robustness.md index 5756cc1..442490b 100644 --- a/phases/euclid/magnification_robustness.md +++ b/tasks/euclid/magnification_robustness.md @@ -1,7 +1,7 @@ -# Euclid — phase 7: how robust are magnification estimates? model-match vs mismatch across the 10 Euclid lenses +# Euclid — how robust are magnification estimates? model-match vs mismatch across the 10 Euclid lenses Project: euclid -Phase: 7 +Summary: How robust are magnification estimates under model match and mismatch State: planned Gates: Witness: all five model rungs run on all 10 simulated lenses (or a documented, justified reduction), source-mismatch and lens-light-leakage effects quantified separately, and a written Delaunay magnification verdict @@ -15,7 +15,8 @@ Migrated-from: PyAutoMind/draft/research/euclid/magnification_robustness.md ## Question -Ready when: euclid 5 accepted. Runs in parallel with euclid 6. +Ready when: `resimulate_fitted_lens_simulator` is accepted. Runs in parallel +with `sersic_index_recovery`. There are known systematics in magnification estimation: when the lens light and source model match the simulated data (Sersics fitted to Sersics) it is fine; when there is mismatch (an MGE @@ -23,8 +24,8 @@ is used) it breaks — for two distinct reasons. An MGE *source* is source-model *lens light* lets lens light "leak" into the source model and corrupt the magnification. The Delaunay source magnification has never been validated and could carry a bug (pixel areas). -The comparison matrix, fitted against the recorded true magnification of the 10 phase-5 -simulations (Sersic lens light + Sersic source): +The comparison matrix, fitted against the recorded true magnification of the 10 +`resimulate_fitted_lens_simulator` simulations (Sersic lens light + Sersic source): 1. **Matched** — Sersic lens light, Sersic source. The control. If it is not fine, nothing below is interpretable. @@ -36,7 +37,7 @@ simulations (Sersic lens light + Sersic source): Definitions matter: there is more than one magnification in play (the point/Hessian value and the area-based ratio `A_img / A_src`). State which is compared at each rung and make sure the -phase-5 truth is the same quantity; a disagreement between the two is itself a finding. +the resimulations' recorded truth is the same quantity; a disagreement between the two is itself a finding. Prior art to consult before designing the runs — do not duplicate it: the cluster epic's `mesh_magnification_correctness` prompt already covers simulate-and-recover magnification @@ -59,7 +60,7 @@ Acceptance, verbatim from the Mind prompt: ## Where to look -- `euclid_dr1_prelim` (project row): the phase-5 simulations and their recorded true magnifications +- `euclid_dr1_prelim` (project row): the resimulations and their recorded true magnifications - Mind `draft/bug/autoarray/delaunay_area_magnification_audit.md` — the source-code half (6c), which this phase hands its Delaunay leg to whatever it shows - Mind `draft/test/workspaces/mesh_magnification_correctness.md` — the cluster epic's diff --git a/phases/euclid/resimulate_fitted_lens_simulator.md b/tasks/euclid/resimulate_fitted_lens_simulator.md similarity index 74% rename from phases/euclid/resimulate_fitted_lens_simulator.md rename to tasks/euclid/resimulate_fitted_lens_simulator.md index 0d59755..6243f82 100644 --- a/phases/euclid/resimulate_fitted_lens_simulator.md +++ b/tasks/euclid/resimulate_fitted_lens_simulator.md @@ -1,7 +1,7 @@ -# Euclid — phase 5: resimulate a fitted Euclid lens, and resimulate the 10 prelim lenses with true magnifications recorded +# Euclid — resimulate a fitted Euclid lens, and resimulate the 10 prelim lenses with true magnifications recorded Project: euclid -Phase: 5 +Summary: Can a fitted Euclid lens be resimulated and recovered State: planned Gates: Witness: a user with a fit result resimulates it in one command; 10 resimulations exist with recorded truths including magnifications, and the prior-edge rule's inferred and simulated Sersic index is recorded per lens @@ -15,7 +15,8 @@ Migrated-from: PyAutoMind/draft/feature/euclid/resimulate_fitted_lens_simulator. ## Question -Ready when: euclid 4 accepted (it needs the 10 real fits as truth inputs). +Ready when: `euclid_dr1_prelim/dr1_prelim_10_lens_science_run` is accepted (it +needs the 10 real fits as truth inputs). Can a Euclid lens we have a result for be resimulated, so the simulated data can be fitted and PyAutoLens tested for recovery of the correct values? Two artefacts, two homes: @@ -29,14 +30,14 @@ and PyAutoLens tested for recovery of the correct values? Two artefacts, two hom results; they live in the science project, not the public repo. Recipe: Sersic lens light, Sersic source (deliberately over-simple for some of the 10, and -that is explicitly fine — phase 6 tests recovery under a *matched* model first). Record the +that is explicitly fine — `sersic_index_recovery` tests recovery under a *matched* model first). Record the true magnification of every simulated lens as a first-class output stored alongside the -dataset — phase 7's entire question depends on that ground truth, so it must not be +dataset — `magnification_robustness`'s entire question depends on that ground truth, so it must not be reconstructible-only-by-rerunning, and it must record enough to distinguish the point and area magnification definitions. Prior-edge correction: many real fits pin `sersic_index = 5` at the prior edge; when a lens's inferred lens-light Sersic index is at that edge, lower it to a value in [2, 4], record both values, and document the rule for choosing the replacement -(fixed 3.0? drawn? population median?) — phase 6's interpretation depends on it. Match the +(fixed 3.0? drawn? population median?) — `sersic_index_recovery`'s interpretation depends on it. Match the real data's PSF, noise and exposure characteristics, and be explicit about which aspect is idealised and which is faithful. @@ -49,13 +50,14 @@ Acceptance, verbatim from the Mind prompt: - The prior-edge rule is applied and both values recorded per lens. - Gates 6a and 6b. -(6a and 6b are Cortex phases 6 and 7.) +(6a and 6b are the Cortex tasks `sersic_index_recovery` and +`magnification_robustness`.) ## Where to look - `euclid_strong_lens_modeling_pipeline`: `scripts/simulator.py`, `smoke_tests.txt` - `euclid_dr1_prelim` (project row): the 10 simulated datasets and their truth table -- `phases/euclid_dr1_prelim/dr1_prelim_10_lens_science_run.md` — the fits these resimulations start from +- `tasks/euclid_dr1_prelim/dr1_prelim_10_lens_science_run.md` — the fits these resimulations start from ## Runs diff --git a/phases/euclid/sersic_index_recovery.md b/tasks/euclid/sersic_index_recovery.md similarity index 74% rename from phases/euclid/sersic_index_recovery.md rename to tasks/euclid/sersic_index_recovery.md index 8baa667..22409f2 100644 --- a/phases/euclid/sersic_index_recovery.md +++ b/tasks/euclid/sersic_index_recovery.md @@ -1,10 +1,10 @@ -# Euclid — phase 6: do we recover Sersic indices? 10 simulated Euclid lenses vs the real prior-edge pile-up +# Euclid — do we recover Sersic indices? 10 simulated Euclid lenses vs the real prior-edge pile-up Project: euclid -Phase: 6 +Summary: Do we recover Sersic indices or is prior pile-up artefact State: planned Gates: -Witness: 10 simulated fits complete with a recovered-vs-input Sersic index table and plot, the same 10 real lenses' prior-edge status established from phase 4, and a written verdict naming the likely cause +Witness: 10 simulated fits complete with a recovered-vs-input Sersic index table and plot, the same 10 real lenses' prior-edge status established from dr1_prelim_10_lens_science_run, and a written verdict naming the likely cause Budget: 24:00 Runs: Ruling: @@ -15,7 +15,7 @@ Migrated-from: PyAutoMind/draft/research/euclid/sersic_index_recovery.md ## Question -Ready when: euclid 5 accepted. +Ready when: `resimulate_fitted_lens_simulator` is accepted. Real Euclid fits pile up at `sersic_index = 5` — the prior's upper edge. Two competing explanations: @@ -25,7 +25,7 @@ explanations: - **Artefact.** A defective PSF model (or another data-side systematic) drives the fit to absorb the mismatch into a high Sersic index. -The phase-5 resimulations are built with indices in [2, 4] and without whatever data defect is +The `resimulate_fitted_lens_simulator` resimulations are built with indices in [2, 4] and without whatever data defect is suspected. If the fits to the *simulated* data recover the input index, the pile-up is an artefact of the real data; if they *also* pile up at 5, the problem is in the model or the fitting, not the data. @@ -35,7 +35,7 @@ have results for 10 lenses and then work out what changes we want to make. Interpretation hazards to respect: N = 10 is small, and a pile-up in 2 of 10 does not distinguish the hypotheses — say up front what fraction would count as a result. Note which -phase-5 replacement-index rule was used and what it costs the inference. Confirm the simulated +resimulation replacement-index rule was used and what it costs the inference. Confirm the simulated fits use the **same** prior as the real fits, or the comparison is not like-for-like. ## Witness @@ -43,7 +43,8 @@ fits use the **same** prior as the real fits, or the comparison is not like-for- Acceptance, verbatim from the Mind prompt: - 10 simulated fits complete, recovered-vs-input table and plot produced. -- The real-lens prior-edge status of the same 10 lenses established from phase 4 output. +- The real-lens prior-edge status of the same 10 lenses established from + `dr1_prelim_10_lens_science_run` output. - A written verdict naming the likely cause and the recommended change, filed as a follow-up prompt if a change is warranted. No fix implemented here. @@ -51,7 +52,7 @@ Acceptance, verbatim from the Mind prompt: - `euclid` (project row): `catalogue/scripts/plot_lens_sersic_index.py` — already plots this population; reuse it rather than hand-rolling a new plot -- `euclid_dr1_prelim` (project row): the phase-5 simulated datasets and their truth table +- `euclid_dr1_prelim` (project row): the simulated datasets and their truth table - `euclid_strong_lens_modeling_pipeline`: the `sersic_lens_model.py` family used on the real data ## Runs diff --git a/phases/euclid_dr1_prelim/dr1_prelim_10_lens_science_run.md b/tasks/euclid_dr1_prelim/dr1_prelim_10_lens_science_run.md similarity index 95% rename from phases/euclid_dr1_prelim/dr1_prelim_10_lens_science_run.md rename to tasks/euclid_dr1_prelim/dr1_prelim_10_lens_science_run.md index 7181d78..2b9a52a 100644 --- a/phases/euclid_dr1_prelim/dr1_prelim_10_lens_science_run.md +++ b/tasks/euclid_dr1_prelim/dr1_prelim_10_lens_science_run.md @@ -1,7 +1,7 @@ -# Euclid_dr1_prelim — phase 4: DR1 prelim science run — 10 real Euclid lenses in euclid_dr1_prelim on RAL +# Euclid_dr1_prelim — DR1 prelim science run — 10 real Euclid lenses in euclid_dr1_prelim on RAL Project: euclid_dr1_prelim -Phase: 4 +Summary: Ten real DR1 lenses fitted from the pipeline repo alone State: submitted Gates: euclid_strong_lens_modeling_pipeline#48, euclid_strong_lens_modeling_pipeline#49 Witness: 10 lenses fitted end to end on RAL from the pipeline repo alone, with a complete catalogue folder (latents present) whose numerics match the 20260623 reference within the tolerance stated before the run @@ -43,7 +43,8 @@ Acceptance, verbatim from the Mind prompt: - A written verdict: **can everything delivered for the DR1 runs out of `Science/euclid` now be delivered from `euclid_strong_lens_modeling_pipeline`?** Any "no" is itself a finding and should feed back into phase 1. -- Gates phase 5 (the resimulations need these results as their truth inputs). +- Gates `euclid/resimulate_fitted_lens_simulator` (the resimulations need these + results as their truth inputs). The catalogue product set is the reference tile's: `lens_mass.csv`, `lens_sersic.csv`, `source_sersic.csv`, `magnitudes.csv`, `model.fits`, `pre_psf.fits`, and the PNG set @@ -57,7 +58,7 @@ The catalogue product set is the reference tile's: `lens_mass.csv`, `lens_sersic — the numerical comparison reference for the same 10 lenses - `autolens_assistant/skills/euclid_{prepare_data,setup_pipeline,model_lens,hpc_runs}.md` - `euclid_strong_lens_modeling_pipeline`: `catalogue/scripts/`, `hpc/batch_cpu/` -- re-homed from `phases/euclid/` on 2026-09-07 — the `euclid` row went dormant, superseded by +- re-homed from `tasks/euclid/` on 2026-09-07 — the `euclid` row went dormant, superseded by `euclid_dr1_prelim`, which this phase births ## Notes diff --git a/phases/ic50_workspace/ep_scale_up.md b/tasks/ic50_workspace/ep_scale_up.md similarity index 94% rename from phases/ic50_workspace/ep_scale_up.md rename to tasks/ic50_workspace/ep_scale_up.md index 71ff637..55f2ea3 100644 --- a/phases/ic50_workspace/ep_scale_up.md +++ b/tasks/ic50_workspace/ep_scale_up.md @@ -1,7 +1,7 @@ -# Ic50_workspace — phase 1: IC50 use case — EP end to end with the existing derived-variable handling +# Ic50_workspace — IC50 use case — EP end to end with the existing derived-variable handling Project: ic50_workspace -Phase: 1 +Summary: Does EP match the graphical joint fit end to end State: planned Gates: Witness: one committed EP-vs-graphical parity table (means ± errors) at small N, EP running at the largest N reached with per-rung timings committed, and any derived-variable or scaling blocker recorded as its own filed prompt diff --git a/phases/inference_programme/cluster_extended_source_inference.md b/tasks/inference_programme/cluster_extended_source_inference.md similarity index 91% rename from phases/inference_programme/cluster_extended_source_inference.md rename to tasks/inference_programme/cluster_extended_source_inference.md index 49abafb..b94b625 100644 --- a/phases/inference_programme/cluster_extended_source_inference.md +++ b/tasks/inference_programme/cluster_extended_source_inference.md @@ -1,7 +1,7 @@ -# Inference_programme — phase 11: cluster extended-source inference — gradient-based fitting building on JAX knowledge +# Inference_programme — cluster extended-source inference — gradient-based fitting building on JAX knowledge Project: inference_programme -Phase: 11 +Summary: Cluster extended source inference testing gradient based fitting State: planned Gates: Witness: a written feasibility verdict with profiling numbers and a go/no-go for a follow-up implementation prompt — not shipped inference machinery diff --git a/phases/inference_programme/cluster_gradient_search_benchmark.md b/tasks/inference_programme/cluster_gradient_search_benchmark.md similarity index 94% rename from phases/inference_programme/cluster_gradient_search_benchmark.md rename to tasks/inference_programme/cluster_gradient_search_benchmark.md index 5e1abba..e15fecc 100644 --- a/phases/inference_programme/cluster_gradient_search_benchmark.md +++ b/tasks/inference_programme/cluster_gradient_search_benchmark.md @@ -1,7 +1,7 @@ -# Inference_programme — phase 17: cluster-scale gradient-search benchmark (Prodigy vs Nautilus, point-source) +# Inference_programme — cluster-scale gradient-search benchmark (Prodigy vs Nautilus, point-source) Project: inference_programme -Phase: 17 +Summary: Does Prodigy beat Nautilus at cluster scale for point sources State: dropped Gates: Witness: a cluster point-source sweep whose `results/searches/…` JSONs carry, per arm, best logL against the truth-instance logL, per-parameter recovery, wall time and evals/steps — the same truth-anchored shape as the 2026-07-31 galaxy-scale rows diff --git a/phases/inference_programme/delaunay_fp64_retro_baseline.md b/tasks/inference_programme/delaunay_fp64_retro_baseline.md similarity index 85% rename from phases/inference_programme/delaunay_fp64_retro_baseline.md rename to tasks/inference_programme/delaunay_fp64_retro_baseline.md index 7b9ba5f..1849461 100644 --- a/phases/inference_programme/delaunay_fp64_retro_baseline.md +++ b/tasks/inference_programme/delaunay_fp64_retro_baseline.md @@ -1,7 +1,7 @@ -# Inference_programme — phase 9: the retro-adopted delaunay_fp64 InferenceRefs_v1 baseline +# Inference_programme — the retro-adopted delaunay_fp64 InferenceRefs_v1 baseline Project: inference_programme -Phase: 9 +Summary: Does the retro-adopted delaunay_fp64 reference baseline still stand State: dropped Gates: Witness: the retro-adopted delaunay_fp64 baseline is a physical solution — checkable by inspecting the run's source-plane reconstruction for the demagnified-source signature @@ -15,11 +15,11 @@ Migrated-from: autolens_profiling/results/notes/inference/DECISIONS.md § 2026-0 ## Question -The twin of phase 8: `results/baselines/InferenceRefs_v1/delaunay_fp64/` was adopted +The twin of `mge_fp64_retro_baseline`: `results/baselines/InferenceRefs_v1/delaunay_fp64/` was adopted retroactively on the same day from an existing Nautilus run. Does *that* adoption stand? The run behind the row is RAL job **339071** (24 Aug 2026) — same identification route as -phase 8: the baseline's `source_artifact` is +`mge_fp64_retro_baseline`: the baseline's `source_artifact` is `results/searches/nautilus/imaging/delaunay/hst/hpc_hpc_a100_fp64.json`, and the only mirror log that writes it is `logs/output/output.339071.out`, whose `Total wall: 1938.52 s` matches the baseline's `total_wall_s: 1938.52`. diff --git a/phases/inference_programme/failed_submissions_342008_10.md b/tasks/inference_programme/failed_submissions_342008_10.md similarity index 93% rename from phases/inference_programme/failed_submissions_342008_10.md rename to tasks/inference_programme/failed_submissions_342008_10.md index b2baa01..ac95c7f 100644 --- a/phases/inference_programme/failed_submissions_342008_10.md +++ b/tasks/inference_programme/failed_submissions_342008_10.md @@ -1,7 +1,7 @@ -# Inference_programme — phase 6: the three submissions that died in a second (CWD trap) +# Inference_programme — the three submissions that died in a second (CWD trap) Project: inference_programme -Phase: 6 +Summary: Why did three submissions die instantly and is anything recoverable State: dropped Gates: Witness: the three submissions produce result rows; they did not — each died in about one second before writing any output diff --git a/phases/inference_programme/inference_refs_v1_redo.md b/tasks/inference_programme/inference_refs_v1_redo.md similarity index 94% rename from phases/inference_programme/inference_refs_v1_redo.md rename to tasks/inference_programme/inference_refs_v1_redo.md index 67942ac..7f489fc 100644 --- a/phases/inference_programme/inference_refs_v1_redo.md +++ b/tasks/inference_programme/inference_refs_v1_redo.md @@ -1,7 +1,7 @@ -# Inference_programme — phase 10: InferenceRefs_v1 redo — the Phase 1 restart wave (array 342091) +# Inference_programme — InferenceRefs_v1 redo — the Phase 1 restart wave (array 342091) Project: inference_programme -Phase: 10 +Summary: Does the refs array deliver clean reference rows after rewind State: accepted Gates: Witness: all 10 array tasks deliver on their own evidence — wall inside the 6:00 budget, a result row carrying version 2026.8.17.1 and a matching target_id, `.err` free of Tracebacks, `.out` ending "Finished." with a `.completed` marker, zero "Fit Already Completed" and no overflow signature @@ -21,7 +21,7 @@ rows on the current stack? Dispatched from the laptop 2026-08-31T21:08Z as RAL job 342091, `sbatch --array=0-8,10 --requeue` from `hpc/batch_gpu/`. Task 9 (`mge_pos`) was held back under -the MGE reuse rule and became phase 7. +the MGE reuse rule and became `mge_pos_ref_reuse`. These are pre-Cortex runs, so they enter the ledger as `legacy` lines with `where:` pointing at the active mirror tree they actually landed in (not a quarantine path) and `pulled_to:` the same. diff --git a/phases/inference_programme/knn_rerun_342016_7.md b/tasks/inference_programme/knn_rerun_342016_7.md similarity index 91% rename from phases/inference_programme/knn_rerun_342016_7.md rename to tasks/inference_programme/knn_rerun_342016_7.md index a74f00e..9940f7f 100644 --- a/phases/inference_programme/knn_rerun_342016_7.md +++ b/tasks/inference_programme/knn_rerun_342016_7.md @@ -1,7 +1,7 @@ -# Inference_programme — phase 3: knn reference re-run after the AdaptSplitPower stack boundary +# Inference_programme — knn reference re-run after the AdaptSplitPower stack boundary Project: inference_programme -Phase: 3 +Summary: Does the capped knn re-run close its evidence deficit State: dropped Gates: Witness: the re-run knn reference row replaces the withdrawn one and its logL deficit against the Prodigy arm bar is measured rather than inferred from a stale row diff --git a/phases/inference_programme/legacy_point_output_sweep.md b/tasks/inference_programme/legacy_point_output_sweep.md similarity index 94% rename from phases/inference_programme/legacy_point_output_sweep.md rename to tasks/inference_programme/legacy_point_output_sweep.md index d54ec7b..bb4d57a 100644 --- a/phases/inference_programme/legacy_point_output_sweep.md +++ b/tasks/inference_programme/legacy_point_output_sweep.md @@ -1,7 +1,7 @@ -# Inference_programme — phase 19: sweep the point-source families out of the RAL active output/ tree into output/legacy_point/ +# Inference_programme — sweep the point-source families out of the RAL active output/ tree into output/legacy_point/ Project: inference_programme -Phase: 19 +Summary: Can spent point-source families be swept out of active output State: planned Gates: autolens_profiling#205 Witness: `output/legacy_point/` exists on RAL and on the laptop mirror holding all three point-source families (`image_plane`/`source_plane` fits, `Cluster`, `point_source`) with their directory structure preserved; the active `output/` tree no longer contains them; the certified `InferenceRefs_v1` mesh rows (R-20260902-01, R-20260904-01) are STILL PRESENT in the active tree and were not moved; a spot check finds any moved run under its new path; nothing deleted; a dated one-line note appended beside the ledger @@ -23,9 +23,9 @@ reuse it, never open a second. **Re-scope 2026-09-04 — the mesh rows STAY.** The 2026-09-01 directive's "all mesh results" clause is **superseded** by the two rulings that certified the -positions-on mesh baselines: **R-20260902-01** (phase 10 — `pos_tauto0.2_f1e8` +positions-on mesh baselines: **R-20260902-01** (`inference_refs_v1_redo` — `pos_tauto0.2_f1e8` is the confirmed physical configuration for a mesh source; a mesh run *with* -`positions.info` is citable, one without is not) and **R-20260904-01** (phase 12 +`positions.info` is citable, one without is not) and **R-20260904-01** (`refs_v1_positions_on_completion` — rows 11–13 joined `InferenceRefs_v1`, now 9 certified baselines). Those mesh rows in the active tree ARE the `InferenceRefs_v1` references the redo cites, so moving them would move exactly the evidence this sweep exists to protect. diff --git a/phases/inference_programme/likelihood_term_levers.md b/tasks/inference_programme/likelihood_term_levers.md similarity index 87% rename from phases/inference_programme/likelihood_term_levers.md rename to tasks/inference_programme/likelihood_term_levers.md index 364481c..b54b0a0 100644 --- a/phases/inference_programme/likelihood_term_levers.md +++ b/tasks/inference_programme/likelihood_term_levers.md @@ -1,7 +1,7 @@ -# Inference_programme — phase 23: likelihood-term levers on mass_pix — slogdet vs cholesky, relative jitter on reg.Adapt +# Inference_programme — likelihood-term levers on mass_pix — slogdet vs cholesky, relative jitter on reg.Adapt Project: inference_programme -Phase: 23 +Summary: Are likelihood terms behind gradient search failure on mass_pix State: planned Gates: Witness: each lever arm runs the same `mass_pix` gradient configuration as phase 22 and delivers on its own artefacts — `.completed`, `positions.info`, `.err` free of Tracebacks, `.out` ending "Finished.", a result row whose `target_id` recomputes from `_targets.py`; and each arm reports, against the phase 22 control it is paired with, the change in NaN-lane count, the change in p_hit against phase 21's `f1e5` max logL, and the change in wall — so that a lever is judged on whether it removes the failure phase 22 measured, not on whether it changes a number @@ -16,7 +16,7 @@ Filed: 2026-09-04 **If the gradient search fails on `mass_pix`, is it the likelihood terms?** -**This phase arms only if phase 22 shows NaN lanes or misses.** If phase 22 hits +**This task arms only if `prodigy_mass_pix` shows NaN lanes or misses.** If it hits 5/5 cleanly there is nothing here to run and the phase is dropped. Two levers, both already suspected, neither ever tested on this target: @@ -35,7 +35,7 @@ Two levers, both already suspected, neither ever tested on this target: `signal_scale` 0.004. If it fires, that is a **PyAutoArray task filed in the Mind**, not a change made here. -**Ready when:** phase 22 rules, and its ruling names a NaN or miss population for +**Ready when:** `prodigy_mass_pix` rules, and its ruling names a NaN or miss population for a lever to act on. Absent that, this phase is dropped rather than run. ## Witness @@ -45,7 +45,7 @@ each lever arm runs the same `mass_pix` gradient configuration as phase 22 and d ## Where to look - `autolens_profiling/results/notes/gradient_slam/LEDGER.md` — the phase table and the inherited rules -- phase 22 (`prodigy_mass_pix`) — the control this phase is paired against; its ruling decides whether this arms at all +- `prodigy_mass_pix` — the control this task is paired against; its ruling decides whether this arms at all - `autolens_profiling#166` — the slogdet-as-PyAutoArray-default reminder this lever folds - `autolens_profiling/results/notes/inference/phase_08_regularization/RESULTS.md` — the retired programme's slogdet A/B, for what was already learned - `PyAutoArray` `constant.py:53` and the `adapt.py` regularization schemes — the absolute `1e-8` lift vs `jitter_relative` diff --git a/phases/inference_programme/mass_pix_gradient_cost_probe.md b/tasks/inference_programme/mass_pix_gradient_cost_probe.md similarity index 92% rename from phases/inference_programme/mass_pix_gradient_cost_probe.md rename to tasks/inference_programme/mass_pix_gradient_cost_probe.md index 4e4842d..2a8711b 100644 --- a/phases/inference_programme/mass_pix_gradient_cost_probe.md +++ b/tasks/inference_programme/mass_pix_gradient_cost_probe.md @@ -1,7 +1,7 @@ -# Inference_programme — phase 20: mass_pix gradient cost probe — forward vs value_and_grad on the A100, strict FD on all 7 params +# Inference_programme — mass_pix gradient cost probe — forward vs value_and_grad on the A100, strict FD on all 7 params Project: inference_programme -Phase: 20 +Summary: Is a mass_pix gradient affordable on an A100 and correct State: dropped Gates: autolens_profiling#218 Witness: one A100 job on the `imaging/mass_pix/hst` cell writes a probe artifact carrying: the forward likelihood cost in ms/eval, the `value_and_grad` cost in ms/eval and their ratio; the jit compile time for each; and a strict finite-difference check of the analytic gradient on all 7 free parameters (Isothermal 5 + ExternalShear 2) that PASSES on every one of them — every FD/analytic pair agreeing to the probe's declared tolerance, no parameter skipped, no NaN or non-finite entry in either gradient; `.err` free of Tracebacks and `.out` ending "Finished." @@ -29,9 +29,9 @@ multi-start gradient search out of reach before it starts. Two things are settled here and nowhere else: 1. **The ratio.** `value_and_grad` / forward, measured on this cell. A ratio - **≲ 4×** is sane and phase 22 is affordable. A ratio near **17×** means the + **≲ 4×** is sane and `prodigy_mass_pix` is affordable. A ratio near **17×** means the CPU anomaly reproduces on the GPU, and that is a **library bug to file in the - Mind before phase 22 runs**, not a cost to accept. + Mind before `prodigy_mass_pix` runs**, not a cost to accept. 2. **The gradient itself.** A strict finite-difference check on all 7 free parameters — the same pattern as `autolens_workspace_test/scripts/imaging/jax_grad/pixelization.py`. A diff --git a/phases/inference_programme/mge_fp64_retro_baseline.md b/tasks/inference_programme/mge_fp64_retro_baseline.md similarity index 93% rename from phases/inference_programme/mge_fp64_retro_baseline.md rename to tasks/inference_programme/mge_fp64_retro_baseline.md index 50d58d3..f9c0460 100644 --- a/phases/inference_programme/mge_fp64_retro_baseline.md +++ b/tasks/inference_programme/mge_fp64_retro_baseline.md @@ -1,7 +1,7 @@ -# Inference_programme — phase 8: the retro-adopted mge_fp64 InferenceRefs_v1 baseline +# Inference_programme — the retro-adopted mge_fp64 InferenceRefs_v1 baseline Project: inference_programme -Phase: 8 +Summary: Does the retro-adopted mge_fp64 reference baseline still stand State: accepted Gates: Witness: the retro-adopted mge_fp64 baseline still stands under the redo standard — no fresh `_ref` run is needed for the InferenceRefs_v1 mge_fp64 row diff --git a/phases/inference_programme/mge_pos_ref_reuse.md b/tasks/inference_programme/mge_pos_ref_reuse.md similarity index 93% rename from phases/inference_programme/mge_pos_ref_reuse.md rename to tasks/inference_programme/mge_pos_ref_reuse.md index 35dd23c..c312310 100644 --- a/phases/inference_programme/mge_pos_ref_reuse.md +++ b/tasks/inference_programme/mge_pos_ref_reuse.md @@ -1,7 +1,7 @@ -# Inference_programme — phase 7: reuse 340210_9 as the mge_pos_fp64 InferenceRefs_v1 reference +# Inference_programme — reuse 340210_9 as the mge_pos_fp64 InferenceRefs_v1 reference Project: inference_programme -Phase: 7 +Summary: Is legacy run 340210_9 adopted as the mge_pos_fp64 reference State: accepted Gates: Witness: the quarantined-but-reusable 340210_9 run is at the same target/config as the `mge_pos_fp64` reference and can be adopted rather than resubmitted diff --git a/phases/inference_programme/multiband_compile_census_completion.md b/tasks/inference_programme/multiband_compile_census_completion.md similarity index 93% rename from phases/inference_programme/multiband_compile_census_completion.md rename to tasks/inference_programme/multiband_compile_census_completion.md index d5efb5b..1a66ffe 100644 --- a/phases/inference_programme/multiband_compile_census_completion.md +++ b/tasks/inference_programme/multiband_compile_census_completion.md @@ -1,7 +1,7 @@ -# Inference_programme — phase 18: multi-band compile census completion — A100/multi-core + hetero GPU rows +# Inference_programme — multi-band compile census completion — A100/multi-core + hetero GPU rows Project: inference_programme -Phase: 18 +Summary: Do the confirmatory multi-band compile census legs change anything State: dropped Gates: Witness: the A100/multi-core and `datacube_img_hetero` GPU rows present in the multi-band census results, each with its backend verified from the results path rather than the SLURM partition, plus a written verdict on the two remaining secondary levers in `scripts/misc/jax_compile/README.md` diff --git a/phases/inference_programme/nautilus_mass_pix_baseline.md b/tasks/inference_programme/nautilus_mass_pix_baseline.md similarity index 89% rename from phases/inference_programme/nautilus_mass_pix_baseline.md rename to tasks/inference_programme/nautilus_mass_pix_baseline.md index 1681c8b..68d2aa5 100644 --- a/phases/inference_programme/nautilus_mass_pix_baseline.md +++ b/tasks/inference_programme/nautilus_mass_pix_baseline.md @@ -1,7 +1,7 @@ -# Inference_programme — phase 21: the Nautilus bar on mass_pix — f1e8 (refs convention) and f1e5 (like-for-like) +# Inference_programme — the Nautilus bar on mass_pix — f1e8 (refs convention) and f1e5 (like-for-like) Project: inference_programme -Phase: 21 +Summary: What Nautilus achieves on mass_pix at f1e8 and f1e5 State: gated Gates: autolens_profiling#218 Witness: both Nautilus arms deliver on their own artefacts — a `.completed` marker and a `positions.info` file in each run dir; `.err` free of Tracebacks; `.out` ends "Finished." with zero "Fit Already Completed"; a result row per arm carrying a `target_id` that recomputes from `_targets.py`; no overflow signature (no finite `log_l` above the Fitness ceiling); each arm physical — `einstein_radius` recovered at 1.60 ± 0.02 with `|ell_comps| < 1`; and max logL, log evidence, likelihood evals and sampler wall recorded for both arms @@ -16,14 +16,14 @@ Filed: 2026-09-04 **What does Nautilus achieve on the `mass_pix` cell, and what does it cost?** -This is the bar phase 22 has to beat. Two arms, both n_live 300, seed 0, fp64, +This is the bar `prodigy_mass_pix` has to beat. Two arms, both n_live 300, seed 0, fp64, A100, the `InferenceRefs_v1` settings otherwise: - **`pos_tauto0.2_f1e8`** — the reference convention every certified mesh baseline uses. Nautilus is measured inert to the factor (Δ logZ ≤ 0.022 nats, R-20260902-10), so this arm is comparable with the whole certified set. -- **`pos_tauto0.2_f1e5`** — the *like-for-like* arm. The gradient search in phase - 22 must run at 1e5 (Gate B pt 2: on MGE, factor 1e8 gives Prodigy 0/4 and 1e5 +- **`pos_tauto0.2_f1e5`** — the *like-for-like* arm. The gradient search in + `prodigy_mass_pix` must run at 1e5 (Gate B pt 2: on MGE, factor 1e8 gives Prodigy 0/4 and 1e5 gives 5/5), so without this arm the headline comparison would be cross-objective. Whether MGE's 1e5 transfers to a mesh likelihood scale is **unmeasured**; the two arms agreeing here is the first evidence either way. @@ -55,8 +55,8 @@ Reuse that issue in `/start_dev`; never open a second. When its PR merges, Because nothing has ever been measured on this cell, this phase's `WALL-BASIS` block states a conservative first-run budget with the basis given as "first run -on this cell, no prior measurement"; it is this phase's own Nautilus wall that -becomes the measured basis phase 22 inherits (the wall basis is measured on the +on this cell, no prior measurement"; it is this task's own Nautilus wall that +becomes the measured basis `prodigy_mass_pix` inherits (the wall basis is measured on the cell itself, never transferred). ## Witness diff --git a/phases/inference_programme/phase2_nss_mainline_gate_a_reuse.md b/tasks/inference_programme/phase2_nss_mainline_gate_a_reuse.md similarity index 98% rename from phases/inference_programme/phase2_nss_mainline_gate_a_reuse.md rename to tasks/inference_programme/phase2_nss_mainline_gate_a_reuse.md index f1c655f..b4a9663 100644 --- a/phases/inference_programme/phase2_nss_mainline_gate_a_reuse.md +++ b/tasks/inference_programme/phase2_nss_mainline_gate_a_reuse.md @@ -1,7 +1,7 @@ -# Inference_programme — phase 13: the legacy MGE NSS-vs-Nautilus evidence behind GATE A +# Inference_programme — the legacy MGE NSS-vs-Nautilus evidence behind GATE A Project: inference_programme -Phase: 13 +Summary: Does the legacy MGE evidence behind GATE A stand State: accepted Gates: Witness: the Phase 2 scan's twelve legacy runs are health-clean on the current stack (2026.8.17.1), and every number GATE A was called on recomputes from their committed result rows diff --git a/phases/inference_programme/phase3_prodigy_reliability_gate_b1_reuse.md b/tasks/inference_programme/phase3_prodigy_reliability_gate_b1_reuse.md similarity index 98% rename from phases/inference_programme/phase3_prodigy_reliability_gate_b1_reuse.md rename to tasks/inference_programme/phase3_prodigy_reliability_gate_b1_reuse.md index b27564e..fb0ddf7 100644 --- a/phases/inference_programme/phase3_prodigy_reliability_gate_b1_reuse.md +++ b/tasks/inference_programme/phase3_prodigy_reliability_gate_b1_reuse.md @@ -1,7 +1,7 @@ -# Inference_programme — phase 14: the legacy MGE Prodigy-reliability evidence behind GATE B part 1 +# Inference_programme — the legacy MGE Prodigy-reliability evidence behind GATE B part 1 Project: inference_programme -Phase: 14 +Summary: Does the legacy MGE Prodigy-reliability evidence behind GATE B1 stand State: accepted Gates: Witness: the 35 legacy Prodigy runs behind Gate B part 1 are health-clean on the current stack (2026.8.17.1), and the hit counts, the 15/15 fresh-seed record and the wall ratio recompute from their committed result rows diff --git a/phases/inference_programme/phase4_positions_gate_b2_reuse.md b/tasks/inference_programme/phase4_positions_gate_b2_reuse.md similarity index 99% rename from phases/inference_programme/phase4_positions_gate_b2_reuse.md rename to tasks/inference_programme/phase4_positions_gate_b2_reuse.md index 2c6be5f..15018fa 100644 --- a/phases/inference_programme/phase4_positions_gate_b2_reuse.md +++ b/tasks/inference_programme/phase4_positions_gate_b2_reuse.md @@ -1,7 +1,7 @@ -# Inference_programme — phase 15: the legacy MGE PositionsLH evidence behind GATE B part 2 +# Inference_programme — the legacy MGE PositionsLH evidence behind GATE B part 2 Project: inference_programme -Phase: 15 +Summary: Does the legacy MGE PositionsLH evidence behind GATE B2 stand State: accepted Gates: Witness: the Stage 2 and Stage 3 legacy runs are health-clean on the current stack (2026.8.17.1) with the one known invalid resume isolated, and the 5/5 vs 2/5 vs 0/4 hit counts behind Gate B part 2 recompute from their committed result rows diff --git a/phases/inference_programme/phase5_mesh_gradient_positions_on.md b/tasks/inference_programme/phase5_mesh_gradient_positions_on.md similarity index 95% rename from phases/inference_programme/phase5_mesh_gradient_positions_on.md rename to tasks/inference_programme/phase5_mesh_gradient_positions_on.md index 3dfd39c..834b26d 100644 --- a/phases/inference_programme/phase5_mesh_gradient_positions_on.md +++ b/tasks/inference_programme/phase5_mesh_gradient_positions_on.md @@ -1,7 +1,7 @@ -# Inference_programme — phase 16: Phase 5 — mesh global gradient searches with PositionsLH at factor 1e5 (the first mesh science since the rewind) +# Inference_programme — Phase 5 — mesh global gradient searches with PositionsLH at factor 1e5 (the first mesh science since the rewind) Project: inference_programme -Phase: 16 +Summary: Does Prodigy reach the physical basin on mesh cells positions-on State: dropped Gates: Witness: every arm delivers on its own artefacts — positions.info present in the run dir; .err free of Tracebacks; .out ends "Finished." with a .completed marker and zero "Fit Already Completed"; result row carries version 2026.8.17.1 and a target_id that recomputes from _targets.py; no overflow signature (no finite log_l above the 1e20 Fitness ceiling, no shell_log_l blow-up). Scoring: a cell's arm HITS when at least one lane's best point lands within 2 nats (the Phase-1 tolerance) of that cell's positions-on Nautilus reference maxLL — delaunay_pos 31,338.43, delaunay_nn_pos 31,351.39, slam_source_pix_pos 31,547.24, slam_source_pix_nn_pos 31,405.63, and pixelization_pos / knn_pos / delaunay_matern_pos at whatever phase 12 (RAL 342241) is ruled to; a lane best point with |e| >= 1 is non-physical and is never counted as a hit. Control: the one-cell Nautilus tauto0.2 f1e5 arm reproduces that cell's f1e8 reference logZ to within 0.1 nats (positions inert on the posterior at 1e5 as well as at 1e8). No legacy_wrong number is a bar. @@ -49,7 +49,8 @@ comparison fails on its own terms and the phase says so rather than scoring the **The bars.** The only bars are the positions-on Nautilus reference rows — `delaunay_pos` 31,338.43, `delaunay_nn_pos` 31,351.39, `slam_source_pix_pos` 31,547.24, `slam_source_pix_nn_pos` 31,405.63 (R-20260902-01), and `pixelization_pos` / `knn_pos` / `delaunay_matern_pos` once -phase 12 (RAL 342241) is ruled. **No `legacy_wrong` number is a bar.** In particular the +`refs_v1_positions_on_completion` (RAL 342241) is ruled. +**No `legacy_wrong` number is a bar.** In particular the Phase 8A/8B mesh arms and the 8B "Prodigy arm 30,557.03" reading are quarantined (`output/legacy_wrong/`) and are read here only for run *design* — lane counts, batching, step rates — never as a comparison. R-20260902-01's binding rule also applies to every arm this @@ -79,8 +80,9 @@ cell with a positions-on reference row, once its autoconv leaf lands: Seven cells, then, not three. Four of them (`delaunay`, `delaunay_nn`, `slam_source_pix`, `slam_source_pix_nn`) already carry a *ruled* bar and were deferred only for want of a gradient leaf; the widened dev leg removes that, so this phase now answers H5.2's DelaunayNN / -kernel-CDF ranking rather than parking it. The three phase-12 cells still wait on their bars, -which is why this phase stays `planned` and gated on phase 12 being ruled. The dev leg +kernel-CDF ranking rather than parking it. The three cells above still wait on their bars, +which is why this task stays `planned` and gated on +`refs_v1_positions_on_completion` being ruled. The dev leg established (in `autolens_profiling`) that `searches/_setup.py`'s `build_for_cell` is sampler-agnostic and already builds all seven cells — the SLaM pair even gets SLaM's own `auto` threshold convention for free — so the leaves are thin `run_search(...)` scripts plus a set of @@ -129,7 +131,7 @@ Quoting a rate across a configuration is exactly what killed RAL 340576 (35 of 3 **What must exist first.** -1. **Phase 12 ruled.** All three in-scope cells take their bar from it; without it the witness +1. **`refs_v1_positions_on_completion` ruled.** All three in-scope cells take their bar from it; without it the witness has no numbers. 2. **The widened dev leg merged** (PyAutoMind prompt `draft/feature/workspaces/phase_5_dev_leg_prepare_the_mesh.md`, filed 2026-09-02 and widened @@ -167,7 +169,7 @@ every arm delivers on its own artefacts — positions.info present in the run di - The bars: `autolens_profiling/results/baselines/InferenceRefs_v1/<key>/` plus `INDEX.json` / `INDEX.md` / `SUBMIT_LIST.md` — `pixelization_pos_fp64`, `knn_pos_fp64`, `delaunay_matern_pos_fp64` once phase 12 is ruled, and the four rows ruled by R-20260902-01 - `autolens_profiling/results/notes/inference/PROGRAMME.md` — "### Phase 5 — Pixelized / mesh global searches with PositionsLH" (the pre-registered design) and the "2026-08-31 REWIND" section - `autolens_profiling/scripts/misc/wall/rates.py` — the step-rate rows the probe must add before the array is sized; `hpc/batch_gpu/submit_search_multi_start_prodigy_phase5_positions_array.sh` once the dev leg lands -- `PyAutoCortex/phases/inference_programme/refs_v1_positions_on_completion.md` (phase 12) — the prerequisite +- `PyAutoCortex/tasks/inference_programme/refs_v1_positions_on_completion.md` — the prerequisite ## Runs diff --git a/phases/inference_programme/prodigy_mass_pix.md b/tasks/inference_programme/prodigy_mass_pix.md similarity index 87% rename from phases/inference_programme/prodigy_mass_pix.md rename to tasks/inference_programme/prodigy_mass_pix.md index ae67dfd..0f68768 100644 --- a/phases/inference_programme/prodigy_mass_pix.md +++ b/tasks/inference_programme/prodigy_mass_pix.md @@ -1,7 +1,7 @@ -# Inference_programme — phase 22: MultiStartProdigy on mass_pix — does a gradient search beat Nautilus in a SLaM mass[1] shape? +# Inference_programme — MultiStartProdigy on mass_pix — does a gradient search beat Nautilus in a SLaM mass[1] shape? Project: inference_programme -Phase: 22 +Summary: Does a gradient search beat Nautilus on mass_pix State: planned Gates: Witness: five `MultiStartProdigy` autoconv seeds (0–4) on the `mass_pix` cell at `pos_tauto0.2_f1e5` each deliver on their own artefacts — `.completed`, `positions.info`, `.err` free of Tracebacks, `.out` ending "Finished.", a result row whose `target_id` recomputes from `_targets.py`, no overflow signature; a seed HITS when its best log-posterior lands within 2 nats of phase 21's `pos_tauto0.2_f1e5` max logL AND its best point is physical (`|ell_comps| < 1`, `einstein_radius` 1.60 ± 0.02); p_hit over the five seeds is reported with its Wilson-95 lower bound, and the per-seed wall is reported against phase 21's sampler wall on the same arm; a lane that ends NaN or pinned is counted and reported, never silently dropped @@ -18,11 +18,11 @@ Filed: 2026-09-04 **This is the headline ruling of the epic.** `MultiStartProdigy` autoconv, n_starts 16, batch_size 4, n_steps ≤ 3000, `pos_tauto0.2_f1e5`, seeds 0–4, on the -same `mass_pix` cell phase 21 measured — lens light fixed to truth, mesh and +same `mass_pix` cell `nautilus_mass_pix_baseline` measured — lens light fixed to truth, mesh and `reg.Adapt` fixed at the certified `slam_source_pix_pos` values, only mass + shear free. -The comparison is against **phase 21's `f1e5` arm** (like for like), with the +The comparison is against **`nautilus_mass_pix_baseline`'s `f1e5` arm** (like for like), with the `f1e8` arm as the tie to the certified set. Two numbers decide it: **p_hit** over five seeds, and **wall against the Nautilus wall on the same arm**. The MGE precedent is Prodigy n256 at 5/5 in 163–297 s against Nautilus's 939 s — 3.1–5.8× @@ -32,9 +32,9 @@ anywhere.** What would make this negative and still informative: lanes that NaN, lanes that pin, or a p_hit that collapses while the wall advantage holds. All three are -counted and reported rather than absorbed, and any of them arms phase 23. +counted and reported rather than absorbed, and any of them arms `likelihood_term_levers`. -**Ready when:** phase 21 rules — its `f1e5` arm is the bar, and its own Nautilus +**Ready when:** `nautilus_mass_pix_baseline` rules — its `f1e5` arm is the bar, and its own Nautilus wall is the measured basis this phase's submit script inherits. Phase 21 is the first science phase of the epic: phase 20, the gradient cost probe, was dropped on 2026-09-04 (R-20260904-04) because the epic measures the number of inference @@ -56,7 +56,7 @@ five `MultiStartProdigy` autoconv seeds (0–4) on the `mass_pix` cell at `pos_t - `inference_programme` (project row): `output/searches/multi_start_prodigy_autoconv/imaging/mass_pix/hst/pos_tauto0.2_f1e5/` — the five run trees, each with `positions.info` - `autolens_profiling/results/searches/multi_start_prodigy_autoconv/imaging/mass_pix/hst/` — the result rows - `logs/output/` and `logs/error/` on the mirror -- phase 21 (`nautilus_mass_pix_baseline`) — the bar this is scored against +- `nautilus_mass_pix_baseline` — the bar this is scored against ## Runs diff --git a/phases/inference_programme/refs_5_6_342016_56.md b/tasks/inference_programme/refs_5_6_342016_56.md similarity index 92% rename from phases/inference_programme/refs_5_6_342016_56.md rename to tasks/inference_programme/refs_5_6_342016_56.md index 1e088c0..f6b983b 100644 --- a/phases/inference_programme/refs_5_6_342016_56.md +++ b/tasks/inference_programme/refs_5_6_342016_56.md @@ -1,7 +1,7 @@ -# Inference_programme — phase 4: InferenceRefs_v1 rows 5 and 6 — slam_source_pix_nn, positions off and on +# Inference_programme — InferenceRefs_v1 rows 5 and 6 — slam_source_pix_nn, positions off and on Project: inference_programme -Phase: 4 +Summary: Do slam_source_pix_nn rows 5 and 6 certify as references State: dropped Gates: Witness: InferenceRefs_v1 rows 5 and 6 (slam_source_pix_nn, positions off and pos_tauto0.2_f1e8) land clean and are certifiable as reference baselines diff --git a/phases/inference_programme/refs_v1_positions_on_completion.md b/tasks/inference_programme/refs_v1_positions_on_completion.md similarity index 90% rename from phases/inference_programme/refs_v1_positions_on_completion.md rename to tasks/inference_programme/refs_v1_positions_on_completion.md index 0ce6ffc..7d2cbdd 100644 --- a/phases/inference_programme/refs_v1_positions_on_completion.md +++ b/tasks/inference_programme/refs_v1_positions_on_completion.md @@ -1,7 +1,7 @@ -# Inference_programme — phase 12: InferenceRefs_v1 positions-on completion — pixelization / knn / delaunay_matern (array tasks 11-13) +# Inference_programme — InferenceRefs_v1 positions-on completion — pixelization / knn / delaunay_matern (array tasks 11-13) Project: inference_programme -Phase: 12 +Summary: Do positions-on replacements give clean references for three mesh cells State: accepted Gates: Witness: all 3 array tasks deliver on their own evidence — wall inside the 6:00 budget, a positions.info file in the run dir, a result row carrying version 2026.8.17.1 and a target_id that recomputes from _targets.py, .err free of Tracebacks, .out ending "Finished." with a .completed marker, zero "Fit Already Completed", no overflow signature; each row's maxLL lands within the 29,800–31,600 band of the accepted positions-on rows rather than 500+ nats below it @@ -14,7 +14,7 @@ Filed: 2026-09-02 ## Question -The Phase 1 redo (phase 10, R-20260902-01) certified four positions-on mesh references and +The Phase 1 redo (`inference_refs_v1_redo`, R-20260902-01) certified four positions-on mesh references and retired the positions-off mesh reference design. Three mesh cells were left without any citable reference because their only 342091 rows ran positions-off: `pixelization` (t0), `knn` (t7) and `delaunay_matern` (t8). Do the positions-on replacements — array tasks 11, 12 diff --git a/phases/inference_programme/rewind_2026_08_31.md b/tasks/inference_programme/rewind_2026_08_31.md similarity index 95% rename from phases/inference_programme/rewind_2026_08_31.md rename to tasks/inference_programme/rewind_2026_08_31.md index de9eeb3..c26fb9a 100644 --- a/phases/inference_programme/rewind_2026_08_31.md +++ b/tasks/inference_programme/rewind_2026_08_31.md @@ -1,7 +1,7 @@ -# Inference_programme — phase 1: programme rewind — quarantine the runs, restart at Phase 1 under batch-and-review +# Inference_programme — programme rewind — quarantine the runs, restart at Phase 1 under batch-and-review Project: inference_programme -Phase: 1 +Summary: Should the programme rewind, quarantine its runs and restart State: dropped Gates: Witness: the mesh/pixelization evidence is out of the active tree: `output/searches/` holds only the point_source and cluster trees, on RAL and on the mirror diff --git a/phases/inference_programme/slogdet_ab_342017.md b/tasks/inference_programme/slogdet_ab_342017.md similarity index 91% rename from phases/inference_programme/slogdet_ab_342017.md rename to tasks/inference_programme/slogdet_ab_342017.md index 94599a2..968732d 100644 --- a/phases/inference_programme/slogdet_ab_342017.md +++ b/tasks/inference_programme/slogdet_ab_342017.md @@ -1,7 +1,7 @@ -# Inference_programme — phase 5: slogdet A/B — Cholesky vs slogdet log-det on slam_source_pix_nn (overflow-flood rung 4) +# Inference_programme — slogdet A/B — Cholesky vs slogdet log-det on slam_source_pix_nn (overflow-flood rung 4) Project: inference_programme -Phase: 5 +Summary: Does slogdet change the fit versus Cholesky on slam_source_pix_nn State: dropped Gates: Witness: the Cholesky and slogdet log-det arms of the same slam_source_pix_nn cell differ (or do not) by more than the run-to-run scatter, giving a verdict on the log-det route diff --git a/phases/inference_programme/smc_probe_342018.md b/tasks/inference_programme/smc_probe_342018.md similarity index 93% rename from phases/inference_programme/smc_probe_342018.md rename to tasks/inference_programme/smc_probe_342018.md index 976ed94..2952942 100644 --- a/phases/inference_programme/smc_probe_342018.md +++ b/tasks/inference_programme/smc_probe_342018.md @@ -1,7 +1,7 @@ -# Inference_programme — phase 2: SMC probe on MGE — warm-start MALA, warm-start HMC, cold MALA +# Inference_programme — SMC probe on MGE — warm-start MALA, warm-start HMC, cold MALA Project: inference_programme -Phase: 2 +Summary: Does the SMC arm reach the Nautilus bar on MGE State: dropped Gates: Witness: the SMC arm's warm-start MALA/HMC rows on MGE reach the Nautilus logZ bar and the acceptance rates are ingestable as a PROGRAMME Phase-7 readout diff --git a/phases/slope_hierarchy/methods_writeup.md b/tasks/slope_hierarchy/methods_writeup.md similarity index 95% rename from phases/slope_hierarchy/methods_writeup.md rename to tasks/slope_hierarchy/methods_writeup.md index a223970..1b6efdc 100644 --- a/phases/slope_hierarchy/methods_writeup.md +++ b/tasks/slope_hierarchy/methods_writeup.md @@ -1,7 +1,7 @@ -# Slope_hierarchy — phase 2: methods write-up (NUTS headline, EP cautionary) +# Slope_hierarchy — methods write-up (NUTS headline, EP cautionary) Project: slope_hierarchy -Phase: 2 +Summary: What does the finished N=5 slope-hierarchy result actually claim State: planned Gates: Witness: prose plus figures and tables committed under `paper/` in the slope_hierarchy checkout, every number regenerated from the committed `results/` JSONs rather than hand-copied, and the scope decision (methods note vs publishable short paper) recorded in the file itself @@ -68,7 +68,7 @@ Two honest shapes, and this should be settled before drafting: - a **methods note / project write-up** in `paper/` for internal use and to ground the PyAutoFit issues; or - a **publishable short paper**, which realistically wants the N=25–50 scale-up - (phase 1, `phases/slope_hierarchy/n25_scale_up.md`) first, since N=5 is thin + (`tasks/slope_hierarchy/n25_scale_up.md`) first, since N=5 is thin for a headline parity claim. Prefer the note unless the scale-up has already run — the note costs a session diff --git a/phases/slope_hierarchy/n25_scale_up.md b/tasks/slope_hierarchy/n25_scale_up.md similarity index 96% rename from phases/slope_hierarchy/n25_scale_up.md rename to tasks/slope_hierarchy/n25_scale_up.md index 1784d43..42266e8 100644 --- a/phases/slope_hierarchy/n25_scale_up.md +++ b/tasks/slope_hierarchy/n25_scale_up.md @@ -1,7 +1,7 @@ -# Slope_hierarchy — phase 1: scale the hierarchical slope recovery to N=25–50 +# Slope_hierarchy — scale the hierarchical slope recovery to N=25–50 Project: slope_hierarchy -Phase: 1 +Summary: Does hierarchical slope recovery hold at N=25 to 50 State: planned Gates: Witness: an N=25–50 parity table in the same shape as the N=5 one, committed under `results/`, plus the scaling measurements (VRAM ceiling, JAX compile time vs model size, sampler ladder at 3N+1 dims, gradient-utilisation sanity) diff --git a/phases/subhalo_validation/delaunay_adapt_split_pl_eff_0.md b/tasks/subhalo_validation/delaunay_adapt_split_pl_eff_0.md similarity index 94% rename from phases/subhalo_validation/delaunay_adapt_split_pl_eff_0.md rename to tasks/subhalo_validation/delaunay_adapt_split_pl_eff_0.md index 9b7a8f9..08b0b65 100644 --- a/phases/subhalo_validation/delaunay_adapt_split_pl_eff_0.md +++ b/tasks/subhalo_validation/delaunay_adapt_split_pl_eff_0.md @@ -1,7 +1,7 @@ -# Subhalo_validation — phase 2: delaunay_adapt_split on pl_eff_0 — the clumpy-source lens +# Subhalo_validation — delaunay_adapt_split on pl_eff_0 — the clumpy-source lens Project: subhalo_validation -Phase: 2 +Summary: Does delaunay_adapt_split false-detect a subhalo on the clumpy source State: submitted Gates: Reset: job B (342234 / 342240) cancelled 2026-09-02 after 12 min at the human's request: the chain waits for the numba likelihood speed-ups in progress; source_lp[1] (job A) is complete and kept; the partial source_pix[1] outputs were removed on RAL diff --git a/phases/subhalo_validation/delaunay_adapt_split_pl_eff_1_outer.md b/tasks/subhalo_validation/delaunay_adapt_split_pl_eff_1_outer.md similarity index 95% rename from phases/subhalo_validation/delaunay_adapt_split_pl_eff_1_outer.md rename to tasks/subhalo_validation/delaunay_adapt_split_pl_eff_1_outer.md index bc21473..a3fa1df 100644 --- a/phases/subhalo_validation/delaunay_adapt_split_pl_eff_1_outer.md +++ b/tasks/subhalo_validation/delaunay_adapt_split_pl_eff_1_outer.md @@ -1,7 +1,7 @@ -# Subhalo_validation — phase 3: delaunay_adapt_split on pl_eff_1_outer — the outer-component lens, plus the AdaptSplit fix rerun +# Subhalo_validation — delaunay_adapt_split on pl_eff_1_outer — the outer-component lens, plus the AdaptSplit fix rerun Project: subhalo_validation -Phase: 3 +Summary: Does delaunay_adapt_split false-detect on the outer-component lens and AdaptSplit fix State: submitted Gates: Reset: job B (342234 / 342240) cancelled 2026-09-02 after 12 min at the human's request: the chain waits for the numba likelihood speed-ups in progress; source_lp[1] (job A) is complete and kept; the partial source_pix[1] outputs were removed on RAL diff --git a/phases/subhalo_validation/delaunay_adapt_split_pl_sersic_0.md b/tasks/subhalo_validation/delaunay_adapt_split_pl_sersic_0.md similarity index 95% rename from phases/subhalo_validation/delaunay_adapt_split_pl_sersic_0.md rename to tasks/subhalo_validation/delaunay_adapt_split_pl_sersic_0.md index afe7bd8..07b2da7 100644 --- a/phases/subhalo_validation/delaunay_adapt_split_pl_sersic_0.md +++ b/tasks/subhalo_validation/delaunay_adapt_split_pl_sersic_0.md @@ -1,7 +1,7 @@ -# Subhalo_validation — phase 1: delaunay_adapt_split on pl_sersic_0 — the false-positive null +# Subhalo_validation — delaunay_adapt_split on pl_sersic_0 — the false-positive null Project: subhalo_validation -Phase: 1 +Summary: Does delaunay_adapt_split false-detect a subhalo on the null lens State: accepted Gates: Reset: job B (342234 / 342240) cancelled 2026-09-02 after 12 min at the human's request: the chain waits for the numba likelihood speed-ups in progress; source_lp[1] (job A) is complete and kept; the partial source_pix[1] outputs were removed on RAL diff --git a/phases/subhalo_validation/rectangular_adapt_pl_eff_0.md b/tasks/subhalo_validation/rectangular_adapt_pl_eff_0.md similarity index 89% rename from phases/subhalo_validation/rectangular_adapt_pl_eff_0.md rename to tasks/subhalo_validation/rectangular_adapt_pl_eff_0.md index c7bec00..6c14705 100644 --- a/phases/subhalo_validation/rectangular_adapt_pl_eff_0.md +++ b/tasks/subhalo_validation/rectangular_adapt_pl_eff_0.md @@ -1,7 +1,7 @@ -# Subhalo_validation — phase 5: rectangular_adapt on pl_eff_0 — the clumpy-source lens on the RectangularBilinear comparison +# Subhalo_validation — rectangular_adapt on pl_eff_0 — the clumpy-source lens on the RectangularBilinear comparison Project: subhalo_validation -Phase: 5 +Summary: Does rectangular_adapt false-detect a subhalo on the clumpy source State: running Gates: Witness: evidence_increase < 5 in results/rectangular_adapt/pl_eff_0_no_subhalo.json (test_mode false; grid-derived, subhalo_stage subhalo[1]) diff --git a/phases/subhalo_validation/rectangular_adapt_pl_eff_1_outer.md b/tasks/subhalo_validation/rectangular_adapt_pl_eff_1_outer.md similarity index 89% rename from phases/subhalo_validation/rectangular_adapt_pl_eff_1_outer.md rename to tasks/subhalo_validation/rectangular_adapt_pl_eff_1_outer.md index 3ca65f6..5b4046f 100644 --- a/phases/subhalo_validation/rectangular_adapt_pl_eff_1_outer.md +++ b/tasks/subhalo_validation/rectangular_adapt_pl_eff_1_outer.md @@ -1,7 +1,7 @@ -# Subhalo_validation — phase 6: rectangular_adapt on pl_eff_1_outer — the outer-component lens on the RectangularBilinear comparison +# Subhalo_validation — rectangular_adapt on pl_eff_1_outer — the outer-component lens on the RectangularBilinear comparison Project: subhalo_validation -Phase: 6 +Summary: Does rectangular_adapt false-detect a subhalo on the outer-component lens State: running Gates: Witness: evidence_increase < 5 in results/rectangular_adapt/pl_eff_1_outer_no_subhalo.json (test_mode false; grid-derived, subhalo_stage subhalo[1]) diff --git a/phases/subhalo_validation/rectangular_adapt_pl_sersic_0.md b/tasks/subhalo_validation/rectangular_adapt_pl_sersic_0.md similarity index 94% rename from phases/subhalo_validation/rectangular_adapt_pl_sersic_0.md rename to tasks/subhalo_validation/rectangular_adapt_pl_sersic_0.md index 79cf044..5d324c5 100644 --- a/phases/subhalo_validation/rectangular_adapt_pl_sersic_0.md +++ b/tasks/subhalo_validation/rectangular_adapt_pl_sersic_0.md @@ -1,7 +1,7 @@ -# Subhalo_validation — phase 4: rectangular_adapt on pl_sersic_0 — the RectangularBilinear comparison +# Subhalo_validation — rectangular_adapt on pl_sersic_0 — the RectangularBilinear comparison Project: subhalo_validation -Phase: 4 +Summary: Does rectangular_adapt behave like delaunay_adapt_split on the clean null State: submitted Gates: Reset: job B (342234 / 342240) cancelled 2026-09-02 after 12 min at the human's request: the chain waits for the numba likelihood speed-ups in progress; source_lp[1] (job A) is complete and kept; the partial source_pix[1] outputs were removed on RAL @@ -26,7 +26,7 @@ rectangular tree, job B (`342095_0`) runs the numba chain `afterok`. It is the f grid rather than a refine and is **not** like-for-like with the three delaunay rows. The second `rectangular_adapt` lens stays PARKED: which `pl_eff` (if either) goes next is a -human decision that follows the rulings on phases 2 and 3. +human decision that follows the rulings on the two `pl_eff` delaunay tasks. Three simulated HST-like lenses (0.05" pixels, 301×301, PSF sigma 0.05", 300 s), identical `PowerLaw` + shear mass and bulge+disk lens light, differing only in the source. The pipeline is diff --git a/tests/fixtures/empty/phases/.gitkeep b/tests/fixtures/empty/tasks/.gitkeep similarity index 100% rename from tests/fixtures/empty/phases/.gitkeep rename to tests/fixtures/empty/tasks/.gitkeep diff --git a/tests/fixtures/skeleton/batches/2026-09-01-pm.md b/tests/fixtures/skeleton/batches/2026-09-01-pm.md index 6448491..c79a606 100644 --- a/tests/fixtures/skeleton/batches/2026-09-01-pm.md +++ b/tests/fixtures/skeleton/batches/2026-09-01-pm.md @@ -5,6 +5,6 @@ field of `rulings/2026/09/R-20260901-03.md` must resolve to a file here. - reviewed-at: 2026-09-01T17:10Z - notes: | - Phase 5's task 9 OOMed before its first step and was resubmitted alone - (342102); the board showed the other nine. Phase 10 counted as not + 05_running_array's array task 9 OOMed before its first step and was resubmitted alone + (342102); the board showed the other nine. 10_dropped counted as not delivered — legacy_wrong, nothing to review but the drop. diff --git a/tests/fixtures/skeleton/rulings/2026/09/R-20260901-01.md b/tests/fixtures/skeleton/rulings/2026/09/R-20260901-01.md index 752bb93..949a81c 100644 --- a/tests/fixtures/skeleton/rulings/2026/09/R-20260901-01.md +++ b/tests/fixtures/skeleton/rulings/2026/09/R-20260901-01.md @@ -1,7 +1,7 @@ -# R-20260901-01 — accept phase 8 +# R-20260901-01 — accept example 08_accepted Project: example -Phase: phases/example/08_accepted.md +Task: tasks/example/08_accepted.md Runs: 342050 Ruling: accept Reviewed-at: 2026-09-01T10:30Z @@ -14,5 +14,5 @@ reproduces the anchor. ## Evidence -- `/mnt/c/Users/Jammy/Science/example/output/phase_08/anchor/samples_summary.json` -- `/mnt/c/Users/Jammy/Science/example/wiki/project/state.md` (phase 8) +- `/mnt/c/Users/Jammy/Science/example/output/task_08/anchor/samples_summary.json` +- `/mnt/c/Users/Jammy/Science/example/wiki/project/state.md` (task 8) diff --git a/tests/fixtures/skeleton/rulings/2026/09/R-20260901-02.md b/tests/fixtures/skeleton/rulings/2026/09/R-20260901-02.md index a5e1164..2884954 100644 --- a/tests/fixtures/skeleton/rulings/2026/09/R-20260901-02.md +++ b/tests/fixtures/skeleton/rulings/2026/09/R-20260901-02.md @@ -1,7 +1,7 @@ -# R-20260901-02 — re-accept phase 8 with the corrected evidence pointer +# R-20260901-02 — re-accept example 08_accepted with the corrected evidence pointer Project: example -Phase: phases/example/08_accepted.md +Task: tasks/example/08_accepted.md Runs: 342050 Ruling: accept Supersedes: R-20260901-01 @@ -12,11 +12,11 @@ Follow-ups: PyAutoLens#901 ## Ruling -Accept, as before. R-20260901-01 cited the wrong summary file (the phase 3 -anchor, not the phase 8 one); the number and the verdict stand. File the +Accept, as before. R-20260901-01 cited the wrong summary file (the task 3 +anchor, not the task 8 one); the number and the verdict stand. File the sampler-default follow-up. ## Evidence -- `/mnt/c/Users/Jammy/Science/example/output/phase_08/anchor/samples_summary.json` -- `/mnt/c/Users/Jammy/Science/example/wiki/project/state.md` (phase 8) +- `/mnt/c/Users/Jammy/Science/example/output/task_08/anchor/samples_summary.json` +- `/mnt/c/Users/Jammy/Science/example/wiki/project/state.md` (task 8) diff --git a/tests/fixtures/skeleton/rulings/2026/09/R-20260901-03.md b/tests/fixtures/skeleton/rulings/2026/09/R-20260901-03.md index 10bbab7..3025454 100644 --- a/tests/fixtures/skeleton/rulings/2026/09/R-20260901-03.md +++ b/tests/fixtures/skeleton/rulings/2026/09/R-20260901-03.md @@ -1,7 +1,7 @@ -# R-20260901-03 — leave phase 5 to finish +# R-20260901-03 — leave example 05_running_array to finish Project: example -Phase: phases/example/05_running_array.md +Task: tasks/example/05_running_array.md Runs: 342091, 342102 Ruling: leave-to-finish Batch: 2026-09-01-pm @@ -15,4 +15,4 @@ alone and will be on the next board. ## Evidence -- `/mnt/c/Users/Jammy/Science/example/output/phase_05/` (nine checkpoints) +- `/mnt/c/Users/Jammy/Science/example/output/task_05/` (nine checkpoints) diff --git a/tests/fixtures/skeleton/rulings/2026/09/R-20260901-04.md b/tests/fixtures/skeleton/rulings/2026/09/R-20260901-04.md index 0ccb1b7..ae4c9b3 100644 --- a/tests/fixtures/skeleton/rulings/2026/09/R-20260901-04.md +++ b/tests/fixtures/skeleton/rulings/2026/09/R-20260901-04.md @@ -1,7 +1,7 @@ -# R-20260901-04 — rerun phase 9 on the released stack +# R-20260901-04 — rerun example 09_rerun on the released stack Project: example -Phase: phases/example/09_rerun.md +Task: tasks/example/09_rerun.md Runs: 341900 Ruling: rerun Batch: 2026-09-01-pm @@ -16,4 +16,4 @@ fix; redo it on the released stack once PyAutoArray#510 lands. ## Evidence -- `/mnt/c/Users/Jammy/Science/example/output/legacy/phase_09/mask.fits` +- `/mnt/c/Users/Jammy/Science/example/output/legacy/task_09/mask.fits` diff --git a/tests/fixtures/skeleton/rulings/2026/09/R-20260901-05.md b/tests/fixtures/skeleton/rulings/2026/09/R-20260901-05.md index bf15b7c..fd8b501 100644 --- a/tests/fixtures/skeleton/rulings/2026/09/R-20260901-05.md +++ b/tests/fixtures/skeleton/rulings/2026/09/R-20260901-05.md @@ -1,7 +1,7 @@ # R-20260901-05 — drop the mesh-pix sweep Project: example -Phase: phases/example/10_dropped.md +Task: tasks/example/10_dropped.md Runs: 341950, 341960 Ruling: drop Batch: 2026-09-01-pm @@ -11,9 +11,9 @@ Review-minutes-actual: 3 ## Ruling Drop. The sweep ran on the wrong regularisation and the question is answered -by phase 8; nothing here is reusable. Revive as a new phase if mesh-pix comes +by task 8; nothing here is reusable. Revive as a new task if mesh-pix comes back. ## Evidence -- `/mnt/c/Users/Jammy/Science/example/output/legacy_wrong/phase_10/` +- `/mnt/c/Users/Jammy/Science/example/output/legacy_wrong/task_10/` diff --git a/tests/fixtures/skeleton/phases/example/01_scope.md b/tests/fixtures/skeleton/tasks/example/01_scope.md similarity index 66% rename from tests/fixtures/skeleton/phases/example/01_scope.md rename to tests/fixtures/skeleton/tasks/example/01_scope.md index 34fa9fb..e3e1899 100644 --- a/tests/fixtures/skeleton/phases/example/01_scope.md +++ b/tests/fixtures/skeleton/tasks/example/01_scope.md @@ -1,7 +1,7 @@ -# Example — phase 1: scope the programme +# Example — scope the programme Project: example -Phase: 1 +Summary: Which lenses anchor the programme and at what pixel scale State: planned Gates: Witness: @@ -16,7 +16,7 @@ Which three lenses anchor the programme, and at what pixel scale? ## Witness -(not yet registered — a planned phase may leave this empty) +(not yet registered — a planned task may leave this empty) ## Where to look diff --git a/tests/fixtures/skeleton/phases/example/02_gated_on_dev.md b/tests/fixtures/skeleton/tasks/example/02_gated_on_dev.md similarity index 62% rename from tests/fixtures/skeleton/phases/example/02_gated_on_dev.md rename to tests/fixtures/skeleton/tasks/example/02_gated_on_dev.md index c987abf..3f466e3 100644 --- a/tests/fixtures/skeleton/phases/example/02_gated_on_dev.md +++ b/tests/fixtures/skeleton/tasks/example/02_gated_on_dev.md @@ -1,7 +1,7 @@ -# Example — phase 2: the pixel-scale sweep, gated on two dev fixes +# Example — the pixel-scale sweep, gated on two dev fixes Project: example -Phase: 2 +Summary: Does the pixel-scale sweep change the preferred model State: gated Gates: PyAutoArray#431, https://github.com/PyAutoLabs/PyAutoFit/pull/1436 Witness: three lenses reach log-evidence within 2.0 of the anchor at 0.05 arcsec @@ -17,11 +17,11 @@ prior-loading fix land? ## Witness -`output/phase_02/*/samples_summary.json` — log-evidence within 2.0 of phase 1. +`output/task_02/*/samples_summary.json` — log-evidence within 2.0 of the anchor. ## Where to look -- `/mnt/c/Users/Jammy/Science/example/output/phase_02/` +- `/mnt/c/Users/Jammy/Science/example/output/task_02/` ## Runs diff --git a/tests/fixtures/skeleton/phases/example/03_ready_cleared.md b/tests/fixtures/skeleton/tasks/example/03_ready_cleared.md similarity index 59% rename from tests/fixtures/skeleton/phases/example/03_ready_cleared.md rename to tests/fixtures/skeleton/tasks/example/03_ready_cleared.md index 60e3a00..bd3dccf 100644 --- a/tests/fixtures/skeleton/phases/example/03_ready_cleared.md +++ b/tests/fixtures/skeleton/tasks/example/03_ready_cleared.md @@ -1,7 +1,7 @@ -# Example — phase 3: the anchor refit, gate cleared +# Example — the anchor refit, gate cleared Project: example -Phase: 3 +Summary: Does the refit reproduce the published anchor State: ready Gates: PyAutoGalaxy#486 Witness: anchor lens theta_E within 0.01 arcsec of the published value @@ -16,11 +16,11 @@ Does the refit on the released stack reproduce the published anchor? ## Witness -`output/phase_03/anchor/samples_summary.json` — `theta_E` within 0.01 arcsec. +`output/task_03/anchor/samples_summary.json` — `theta_E` within 0.01 arcsec. ## Where to look -- `/mnt/c/Users/Jammy/Science/example/output/phase_03/` +- `/mnt/c/Users/Jammy/Science/example/output/task_03/` ## Runs diff --git a/tests/fixtures/skeleton/phases/example/04_submitted_override.md b/tests/fixtures/skeleton/tasks/example/04_submitted_override.md similarity index 67% rename from tests/fixtures/skeleton/phases/example/04_submitted_override.md rename to tests/fixtures/skeleton/tasks/example/04_submitted_override.md index 33cfe48..6872427 100644 --- a/tests/fixtures/skeleton/phases/example/04_submitted_override.md +++ b/tests/fixtures/skeleton/tasks/example/04_submitted_override.md @@ -1,7 +1,7 @@ -# Example — phase 4: the two-band fit, submitted under a gate override +# Example — the two-band fit, submitted under a gate override Project: example -Phase: 4 +Summary: Do the two bands agree on the source centre State: submitted Gates: PyAutoLens#900 Witness: both bands converge to the same source centre within 0.02 arcsec @@ -17,11 +17,11 @@ Do the two bands agree on the source centre once fitted jointly? ## Witness -`output/phase_04/joint/samples_summary.json` — source centres within 0.02 arcsec. +`output/task_04/joint/samples_summary.json` — source centres within 0.02 arcsec. ## Where to look -- `/mnt/c/Users/Jammy/Science/example/output/phase_04/` +- `/mnt/c/Users/Jammy/Science/example/output/task_04/` ## Runs diff --git a/tests/fixtures/skeleton/phases/example/05_running_array.md b/tests/fixtures/skeleton/tasks/example/05_running_array.md similarity index 70% rename from tests/fixtures/skeleton/phases/example/05_running_array.md rename to tests/fixtures/skeleton/tasks/example/05_running_array.md index 1c46ca6..9845d64 100644 --- a/tests/fixtures/skeleton/phases/example/05_running_array.md +++ b/tests/fixtures/skeleton/tasks/example/05_running_array.md @@ -1,7 +1,7 @@ -# Example — phase 5: the nine-lens array +# Example — the nine-lens array Project: example -Phase: 5 +Summary: Does Delaunay reach the same theta_E basin on ten lenses State: running Gates: PyAutoArray#431 Witness: nine of ten array tasks write a sane checkpoint.hdf5 within 8:00 wall @@ -18,16 +18,16 @@ Does the Delaunay pipeline reach the same theta_E basin on all ten lenses? ## Witness -`output/phase_05/*/checkpoint.hdf5` present for nine lenses, `.err` clean. +`output/task_05/*/checkpoint.hdf5` present for nine lenses, `.err` clean. ## Where to look -- `/mnt/c/Users/Jammy/Science/example/output/phase_05/` +- `/mnt/c/Users/Jammy/Science/example/output/task_05/` ## Runs - 342091_[0-8,10]: done — gpu — submitted 2026-08-30 — wall 6:12 - pulled_to: /mnt/c/Users/Jammy/Science/example/output/phase_05 + pulled_to: /mnt/c/Users/Jammy/Science/example/output/task_05 - 342091_9: failed — gpu — submitted 2026-08-30 — wall 0:00 — OOM before the first step - 342102: running — gpu — submitted 2026-09-01 — wall 0:00 — task 9 resubmitted alone after: 342091_9 diff --git a/tests/fixtures/skeleton/phases/example/06_pulled.md b/tests/fixtures/skeleton/tasks/example/06_pulled.md similarity index 53% rename from tests/fixtures/skeleton/phases/example/06_pulled.md rename to tests/fixtures/skeleton/tasks/example/06_pulled.md index 28f5e92..cfa9e43 100644 --- a/tests/fixtures/skeleton/phases/example/06_pulled.md +++ b/tests/fixtures/skeleton/tasks/example/06_pulled.md @@ -1,7 +1,7 @@ -# Example — phase 6: the first half of the shared array, pulled +# Example — the first half of the shared array, pulled Project: example -Phase: 6 +Summary: Do the five bright lenses converge under the fast preset State: pulled Witness: five lenses' checkpoint.hdf5 sane and .err clean under 4:00 wall Budget: 4:00 @@ -16,16 +16,16 @@ Do the five bright lenses converge under the fast preset? ## Witness -`output/phase_06/*/checkpoint.hdf5` sane for all five. +`output/task_06/*/checkpoint.hdf5` sane for all five. ## Where to look -- `/mnt/c/Users/Jammy/Science/example/output/phase_06/` +- `/mnt/c/Users/Jammy/Science/example/output/task_06/` ## Runs -- 342120_[0-4]: done — gpu — submitted 2026-08-31 — wall 3:40 — array shared with phase 7 - pulled_to: /mnt/c/Users/Jammy/Science/example/output/phase_06 +- 342120_[0-4]: done — gpu — submitted 2026-08-31 — wall 3:40 — array shared with 07_awaiting_ruling + pulled_to: /mnt/c/Users/Jammy/Science/example/output/task_06 ## Ruling diff --git a/tests/fixtures/skeleton/phases/example/07_awaiting_ruling.md b/tests/fixtures/skeleton/tasks/example/07_awaiting_ruling.md similarity index 62% rename from tests/fixtures/skeleton/phases/example/07_awaiting_ruling.md rename to tests/fixtures/skeleton/tasks/example/07_awaiting_ruling.md index af39c57..3bb82c3 100644 --- a/tests/fixtures/skeleton/phases/example/07_awaiting_ruling.md +++ b/tests/fixtures/skeleton/tasks/example/07_awaiting_ruling.md @@ -1,7 +1,7 @@ -# Example — phase 7: the second half of the shared array, awaiting a ruling +# Example — the second half of the shared array, awaiting a ruling Project: example -Phase: 7 +Summary: Do the five faint lenses converge once resumed State: awaiting-ruling Witness: five faint lenses' checkpoint.hdf5 sane after the checkpoint resubmit Budget: 8:00 @@ -16,18 +16,18 @@ Do the five faint lenses converge once resumed from the timed-out checkpoint? ## Witness -`output/phase_07/*/checkpoint.hdf5` sane for all five. +`output/task_07/*/checkpoint.hdf5` sane for all five. ## Where to look -- `/mnt/c/Users/Jammy/Science/example/output/phase_07/` +- `/mnt/c/Users/Jammy/Science/example/output/task_07/` ## Runs - 342110: timeout — gpu — submitted 2026-08-30 — wall 8:00 — hit the budget; resubmitted from checkpoint -- 342120_[5-9]: done — gpu — submitted 2026-08-31 — wall 3:55 — array shared with phase 6 +- 342120_[5-9]: done — gpu — submitted 2026-08-31 — wall 3:55 — array shared with 06_pulled resumes: 342110 - pulled_to: /mnt/c/Users/Jammy/Science/example/output/phase_07 + pulled_to: /mnt/c/Users/Jammy/Science/example/output/task_07 ## Ruling diff --git a/tests/fixtures/skeleton/phases/example/08_accepted.md b/tests/fixtures/skeleton/tasks/example/08_accepted.md similarity index 63% rename from tests/fixtures/skeleton/phases/example/08_accepted.md rename to tests/fixtures/skeleton/tasks/example/08_accepted.md index 4831c41..84897bf 100644 --- a/tests/fixtures/skeleton/phases/example/08_accepted.md +++ b/tests/fixtures/skeleton/tasks/example/08_accepted.md @@ -1,7 +1,7 @@ -# Example — phase 8: the anchor comparison, accepted +# Example — the anchor comparison, accepted Project: example -Phase: 8 +Summary: Does the new sampler reproduce the anchor State: accepted Gates: PyAutoFit#1400 Witness: anchor theta_E within 0.01 arcsec of the published value on the new sampler @@ -18,16 +18,16 @@ Does the new sampler reproduce the anchor within the published error? ## Witness -`output/phase_08/anchor/samples_summary.json` — `theta_E` within 0.01 arcsec. +`output/task_08/anchor/samples_summary.json` — `theta_E` within 0.01 arcsec. ## Where to look -- `/mnt/c/Users/Jammy/Science/example/output/phase_08/` +- `/mnt/c/Users/Jammy/Science/example/output/task_08/` ## Runs - 342050: done — gpu — submitted 2026-08-28 — wall 5:10 - pulled_to: /mnt/c/Users/Jammy/Science/example/output/phase_08 + pulled_to: /mnt/c/Users/Jammy/Science/example/output/task_08 ruled: R-20260901-02 ## Ruling diff --git a/tests/fixtures/skeleton/phases/example/09_rerun.md b/tests/fixtures/skeleton/tasks/example/09_rerun.md similarity index 64% rename from tests/fixtures/skeleton/phases/example/09_rerun.md rename to tests/fixtures/skeleton/tasks/example/09_rerun.md index 23e3876..c260297 100644 --- a/tests/fixtures/skeleton/phases/example/09_rerun.md +++ b/tests/fixtures/skeleton/tasks/example/09_rerun.md @@ -1,7 +1,7 @@ -# Example — phase 9: the legacy-born mask test, ruled rerun +# Example — the legacy-born mask test, ruled rerun Project: example -Phase: 9 +Summary: Is the pre-Cortex mask-edge run reusable State: rerun Witness: the legacy run's mask edge matches the released mask within one pixel Budget: 8:00 @@ -17,16 +17,16 @@ Is the pre-Cortex mask-edge run reusable, or must it be redone on the released s ## Witness -`output/legacy/phase_09/mask.fits` edge vs the released mask. +`output/legacy/task_09/mask.fits` edge vs the released mask. ## Where to look -- `/mnt/c/Users/Jammy/Science/example/output/legacy/phase_09/` +- `/mnt/c/Users/Jammy/Science/example/output/legacy/task_09/` ## Runs - 341900: legacy — gpu — submitted 2026-08-20 — wall 7:50 — pre-Cortex run, quarantined as reusable - where: /mnt/c/Users/Jammy/Science/example/output/legacy/phase_09 + where: /mnt/c/Users/Jammy/Science/example/output/legacy/task_09 ruled: R-20260901-04 ## Ruling diff --git a/tests/fixtures/skeleton/phases/example/10_dropped.md b/tests/fixtures/skeleton/tasks/example/10_dropped.md similarity index 70% rename from tests/fixtures/skeleton/phases/example/10_dropped.md rename to tests/fixtures/skeleton/tasks/example/10_dropped.md index 9528479..b4bad40 100644 --- a/tests/fixtures/skeleton/phases/example/10_dropped.md +++ b/tests/fixtures/skeleton/tasks/example/10_dropped.md @@ -1,7 +1,7 @@ -# Example — phase 10: the mesh-pix sweep, dropped +# Example — the mesh-pix sweep, dropped Project: example -Phase: 10 +Summary: Does the mesh-pix sweep beat the Delaunay anchor State: dropped Witness: mesh-pix log-evidence beats the Delaunay anchor by 5.0 Budget: 6:00 @@ -17,16 +17,16 @@ Does the mesh-pix sweep beat the Delaunay anchor? ## Witness -`output/legacy_wrong/phase_10/*/samples_summary.json` log-evidence vs the anchor. +`output/legacy_wrong/task_10/*/samples_summary.json` log-evidence vs the anchor. ## Where to look -- `/mnt/c/Users/Jammy/Science/example/output/legacy_wrong/phase_10/` +- `/mnt/c/Users/Jammy/Science/example/output/legacy_wrong/task_10/` ## Runs - 341950_[0-3]: legacy_wrong — gpu — submitted 2026-08-22 — wall 6:00 — ran on the wrong regularisation - where: /mnt/c/Users/Jammy/Science/example/output/legacy_wrong/phase_10 + where: /mnt/c/Users/Jammy/Science/example/output/legacy_wrong/task_10 - 341960: void — gpu — submitted 2026-08-23 — wall 0:00 — cancelled before the first step ## Ruling diff --git a/tests/test_cortex.py b/tests/test_cortex.py index 37e9ed4..ffb9f0a 100644 --- a/tests/test_cortex.py +++ b/tests/test_cortex.py @@ -3,7 +3,7 @@ Two things these tests deliberately do, matching the Mind's `test_lifecycle_check.py`: 1. **The fixture is the witness.** `tests/fixtures/skeleton/` holds one project, - one phase per state, five rulings (one chain of two), one batch record and + one task per state, five rulings (one chain of two), one batch record and one review; `tests/fixtures/empty/` is an empty map. Both must pass `check` unchanged, and every writing verb runs on a `copytree` of the skeleton. 2. **Prove each leg FAILS.** Every `check` rule in REFERENCE.md is driven with @@ -29,7 +29,7 @@ EMPTY = REPO / "tests" / "fixtures" / "empty" TODAY = "2026-09-02" -P = {n: f"phases/example/{n}.md" for n in ( +P = {n: f"tasks/example/{n}.md" for n in ( "01_scope", "02_gated_on_dev", "03_ready_cleared", "04_submitted_override", "05_running_array", "06_pulled", "07_awaiting_ruling", "08_accepted", "09_rerun", "10_dropped")} @@ -111,7 +111,7 @@ def test_check_reports_drift_in_lifecycle_shape(tmp_path): assert result.returncode == 1 lines = result.stdout.splitlines() assert lines[0] == "cortex check: DRIFT" - assert lines[1].startswith(" - phases/example/03_ready_cleared.md: ") + assert lines[1].startswith(" - tasks/example/03_ready_cleared.md: ") def test_no_import_time_side_effects_and_root_on_every_verb(): @@ -126,12 +126,12 @@ def test_no_import_time_side_effects_and_root_on_every_verb(): # --------------------------------------------------------------------------- # def test_header_first_occurrence_wins_block_ends_at_blank_line_list_keys(): text = ("# Title\n\nProject: one\nProject: two\nGates:\n- A#1\n- B#2\nState: ready\n\n" - "Phase: 99\n") + "Task: 99\n") title, fields = cortex.parse_header(text) assert title == "Title" assert fields["Project"] == "one" assert fields["Gates"] == "A#1, B#2" - assert "Phase" not in fields # after the blank line — body, not header + assert "Task" not in fields # after the blank line — body, not header def test_header_beyond_30_lines_is_body(): @@ -189,9 +189,9 @@ def test_comment_only_projects_yaml_parses_to_an_empty_map(): def test_the_live_body_map_parses_clean(): - """The seeded map (phase 3) is the real witness for the field validation: + """The seeded map (task 3) is the real witness for the field validation: it is the only file that exercises `note` and a 17-verb list. It stopped - exercising `planned` on 2026-09-07, when euclid_dr1_prelim's phase 4 + exercising `planned` on 2026-09-07, when euclid_dr1_prelim's task 4 launched and its row flipped to `active` — the last `planned` row in the live map. `planned` as a legal status is covered on the fixture by `test_planned_and_retired_are_legal_statuses`.""" @@ -288,27 +288,27 @@ def test_note_is_optional_but_never_empty_and_the_field_set_still_closes(tmp_pat def _quiesce(root: Path, project: str = "example") -> "list[str]": - """Leave `project` holding only ruled and planned phases — dropping the - rulings that named the phases removed, so `check` stays clean.""" + """Leave `project` holding only ruled and planned tasks — dropping the + rulings that named the tasks removed, so `check` stays clean.""" gone = [] - for p in sorted((root / "phases" / project).glob("*.md")): + for p in sorted((root / "tasks" / project).glob("*.md")): if p.stem not in _QUIET: gone.append(p.relative_to(root).as_posix()) p.unlink() for r in sorted((root / "rulings").rglob("*.md")): - if any(f"Phase: {g}" in r.read_text() for g in gone): + if any(f"Task: {g}" in r.read_text() for g in gone): r.unlink() assert _problems(root) == [], _problems(root) return gone def test_retire_flips_the_status_and_stamps_the_note(tmp_path): - """Two lines change and nothing else — the row, its phases and its + """Two lines change and nothing else — the row, its tasks and its rulings all stay, and `check` is clean afterwards.""" root = _copy(tmp_path) _quiesce(root) before = (root / "projects.yaml").read_text().split("\n") - phases_before = sorted(p.name for p in (root / "phases" / "example").glob("*.md")) + tasks_before = sorted(p.name for p in (root / "tasks" / "example").glob("*.md")) assert cortex.retire_project(root, "example", "the question was answered", date(2026, 9, 4)) == "retired example" after = (root / "projects.yaml").read_text().split("\n") @@ -318,8 +318,8 @@ def test_retire_flips_the_status_and_stamps_the_note(tmp_path): # carries no `note:`, so the note is an appended line. assert [ln for ln in after if not ln.startswith((" status:", " note:"))] == \ [ln for ln in before if not ln.startswith(" status:")] - assert sorted(p.name for p in (root / "phases" / "example").glob("*.md")) == \ - phases_before + assert sorted(p.name for p in (root / "tasks" / "example").glob("*.md")) == \ + tasks_before assert _problems(root) == [] rows, problems = cortex.parse_projects((root / "projects.yaml").read_text()) assert problems == [] and rows["example"]["status"] == "retired" @@ -340,7 +340,7 @@ def test_retire_rewrites_an_existing_note_in_place(tmp_path): assert _problems(root) == [] -def test_retire_refuses_while_a_phase_is_still_live(tmp_path): +def test_retire_refuses_while_a_task_is_still_live(tmp_path): """Every state outside `accepted | rerun | dropped | planned` is an unfinished question, and the refusal names each one.""" root = _copy(tmp_path) @@ -351,10 +351,10 @@ def test_retire_refuses_while_a_phase_is_still_live(tmp_path): assert "still has live work" in message for name in ("02_gated_on_dev", "03_ready_cleared", "04_submitted_override", "05_running_array", "06_pulled", "07_awaiting_ruling"): - assert f"phases/example/{name}.md — " in message + assert f"tasks/example/{name}.md — " in message # planned and the ruled states are not named — they are allowed to stay for name in ("01_scope", "08_accepted", "09_rerun", "10_dropped"): - assert f"phases/example/{name}.md" not in message + assert f"tasks/example/{name}.md" not in message assert (root / "projects.yaml").read_text() == before @@ -394,20 +394,50 @@ def test_retire_round_trips_through_the_cli(tmp_path): # --------------------------------------------------------------------------- # -# check — phases +# check — tasks # --------------------------------------------------------------------------- # -def test_duplicate_phase_number(tmp_path): +def test_summary_is_required(tmp_path): + """`Summary:` is the board's line: a task without one is drift, and so is + a task whose `Summary:` is present but empty.""" + root = _copy(tmp_path) + _edit(root, P["02_gated_on_dev"], + "Summary: Does the pixel-scale sweep change the preferred model\n", "") + _assert_drift(root, "02_gated_on_dev.md: missing header key Summary:") + root = _copy(tmp_path / "b") + _edit(root, P["02_gated_on_dev"], + "Summary: Does the pixel-scale sweep change the preferred model\n", "Summary:\n") + _assert_drift(root, "02_gated_on_dev.md: missing header key Summary:") + + +def test_summary_is_capped_at_ten_words(tmp_path): + """Ten words is the cap the board is readable at; eleven is drift.""" + root = _copy(tmp_path) + ten = " ".join(f"w{i}" for i in range(10)) + _edit(root, P["02_gated_on_dev"], + "Summary: Does the pixel-scale sweep change the preferred model\n", + f"Summary: {ten}\n") + assert _problems(root) == [] + _edit(root, P["02_gated_on_dev"], f"Summary: {ten}\n", f"Summary: {ten} w10\n") + _assert_drift(root, "02_gated_on_dev.md: Summary: 11 words — at most 10") + + +def test_phase_header_is_dead(tmp_path): + """The number went on 2026-09-07: `Phase:` is named as retired rather than + passing as a generic unknown key, on a task file and on a ruling alike.""" root = _copy(tmp_path) - _edit(root, P["02_gated_on_dev"], "Phase: 2\n", "Phase: 1\n") - _assert_drift(root, "02_gated_on_dev.md: duplicate Phase: 1 (also phases/example/01_scope.md)") + _edit(root, P["01_scope"], "State: planned\n", "Phase: 1\nState: planned\n") + _edit(root, R + "R-20260901-05.md", "Task: tasks/example/10_dropped.md\n", + "Phase: tasks/example/10_dropped.md\nTask: tasks/example/10_dropped.md\n") + _assert_drift(root, "01_scope.md: Phase: is a retired header", + "R-20260901-05.md: Phase: is a retired header") def test_unknown_project(tmp_path): root = _copy(tmp_path) - shutil.move(root / "phases" / "example", root / "phases" / "other") - for p in (root / "phases" / "other").glob("*.md"): + shutil.move(root / "tasks" / "example", root / "tasks" / "other") + for p in (root / "tasks" / "other").glob("*.md"): p.write_text(p.read_text().replace("Project: example", "Project: other")) - _assert_drift(root, "phases/other/01_scope.md: Project: other is not a projects.yaml key") + _assert_drift(root, "tasks/other/01_scope.md: Project: other is not a projects.yaml key") def test_directory_must_equal_project(tmp_path): @@ -420,7 +450,7 @@ def test_directory_must_equal_project(tmp_path): def test_where_to_look_must_name_somewhere_past_planned(tmp_path): """The section is rendered, not just parsed — the dashboard's `## By project` view and `checkin` print these bullets verbatim as the - folders to open. A phase that has left `planned` carrying only the + folders to open. A task that has left `planned` carrying only the template placeholder names nowhere.""" root = _copy(tmp_path) holder = f"- {cortex.WHERE_PLACEHOLDER}\n" @@ -429,16 +459,17 @@ def test_where_to_look_must_name_somewhere_past_planned(tmp_path): holder) assert _problems(root) == [] _edit(root, P["07_awaiting_ruling"], - "- `/mnt/c/Users/Jammy/Science/example/output/phase_07/`\n", holder) + "- `/mnt/c/Users/Jammy/Science/example/output/task_07/`\n", holder) _assert_drift(root, "07_awaiting_ruling.md: ## Where to look names nowhere") -def test_a_new_phase_ships_the_placeholder_and_no_other_bullet(tmp_path): - """`new` writes `planned`, so the phase it writes passes `check` — and +def test_a_new_task_ships_the_placeholder_and_no_other_bullet(tmp_path): + """`new` writes `planned`, so the task it writes passes `check` — and the placeholder it writes is the exact string the rule exempts.""" root = _copy(tmp_path) - _run("new", "example", "11_fresh", "--phase", "11", "--today", TODAY, root=root) - text = (root / "phases/example/11_fresh.md").read_text() + _run("new", "example", "11_fresh", "--summary", "Does the fresh idea hold", + "--today", TODAY, root=root) + text = (root / "tasks/example/11_fresh.md").read_text() assert f"- {cortex.WHERE_PLACEHOLDER}" in text assert _problems(root) == [] @@ -447,11 +478,11 @@ def test_illegal_state_unknown_key_and_missing_section(tmp_path): root = _copy(tmp_path) _edit(root, P["01_scope"], "State: planned\n", "State: queued\nGate-cleared: x\n") _edit(root, P["01_scope"], "## Where to look", "## Where") - _assert_drift(root, "State: 'queued' is not a phase state", "unknown header key Gate-cleared:", + _assert_drift(root, "State: 'queued' is not a task state", "unknown header key Gate-cleared:", "missing section ## Where to look") -def test_migrated_from_is_legal_on_a_phase_and_a_ruling(tmp_path): +def test_migrated_from_is_legal_on_a_task_and_a_ruling(tmp_path): """Decision 53: the provenance key joins both key tables; everything else outside them is still drift.""" root = _copy(tmp_path) @@ -488,7 +519,7 @@ def test_witness_invariant(tmp_path, rel): _assert_drift(root, f"{rel}: State: {_fields(root, rel)['State']} needs a Witness:") -def test_planned_phase_may_leave_the_witness_empty(): +def test_planned_task_may_leave_the_witness_empty(): assert _fields(SKELETON, P["01_scope"])["Witness"] == "" assert _problems(SKELETON) == [] @@ -520,8 +551,8 @@ def test_bare_stem_overlaps_every_task_of_that_stem(tmp_path): _assert_drift(root, "run 342091_[0-8,10] overlaps 342091 on stem 342091") -def test_one_array_may_feed_two_phases(): - """Job ids are unique per phase, not globally: 342120 sits in phases 6 and 7.""" +def test_one_array_may_feed_two_tasks(): + """Job ids are unique per task, not globally: 342120 sits in tasks 6 and 7.""" assert _fields(SKELETON, P["06_pulled"])["Runs"] == "342120" assert "342120" in _fields(SKELETON, P["07_awaiting_ruling"])["Runs"] assert _problems(SKELETON) == [] @@ -538,21 +569,21 @@ def test_run_line_that_does_not_parse_and_stray_continuation(tmp_path): def test_pulled_needs_a_pulled_to(tmp_path): root = _copy(tmp_path) - _edit(root, P["06_pulled"], " pulled_to: /mnt/c/Users/Jammy/Science/example/output/phase_06\n", "") + _edit(root, P["06_pulled"], " pulled_to: /mnt/c/Users/Jammy/Science/example/output/task_06\n", "") _assert_drift(root, "06_pulled.md: State: pulled needs at least one done | legacy run with pulled_to:") def test_legacy_needs_where(tmp_path): root = _copy(tmp_path) - _edit(root, P["10_dropped"], " where: /mnt/c/Users/Jammy/Science/example/output/legacy_wrong/phase_10\n", "") + _edit(root, P["10_dropped"], " where: /mnt/c/Users/Jammy/Science/example/output/legacy_wrong/task_10\n", "") _assert_drift(root, "10_dropped.md: run 341950_[0-3] is legacy_wrong without where:") -def test_after_and_resumes_name_a_run_of_the_same_phase(tmp_path): +def test_after_and_resumes_name_a_run_of_the_same_task(tmp_path): root = _copy(tmp_path) _edit(root, P["05_running_array"], " after: 342091_9", " after: 342091") _edit(root, P["07_awaiting_ruling"], " resumes: 342110", " resumes: 342120_[5-9]") - _assert_drift(root, "05_running_array.md: run 342102 after: 342091 names no other run of this phase", + _assert_drift(root, "05_running_array.md: run 342102 after: 342091 names no other run of this task", "07_awaiting_ruling.md: run 342120_[5-9] resumes: 342120_[5-9] names no other run") @@ -570,10 +601,10 @@ def test_pulled_with_a_live_run_needs_leave_to_finish(tmp_path): _assert_drift(root, "05_running_array.md: State: pulled with a live run needs a leave-to-finish ruling head") -def test_stray_file_under_phases(tmp_path): +def test_stray_file_under_tasks(tmp_path): root = _copy(tmp_path) - (root / "phases" / "notes.md").write_text("# stray\n") - _assert_drift(root, "phases/notes.md: not a phase path") + (root / "tasks" / "notes.md").write_text("# stray\n") + _assert_drift(root, "tasks/notes.md: not a task path") # --------------------------------------------------------------------------- # @@ -594,11 +625,11 @@ def test_self_supersedes(tmp_path): _assert_drift(root, "R-20260901-02.md: Supersedes: itself") -def test_supersedes_must_be_earlier_and_same_phase(tmp_path): +def test_supersedes_must_be_earlier_and_same_task(tmp_path): root = _copy(tmp_path) _edit(root, R + "R-20260901-01.md", "Ruling: accept\n", "Ruling: accept\nSupersedes: R-20260901-03\n") _assert_drift(root, "R-20260901-01.md: Supersedes: R-20260901-03 is not earlier than R-20260901-01", - "R-20260901-01.md: Supersedes: R-20260901-03 names a different project/phase") + "R-20260901-01.md: Supersedes: R-20260901-03 names a different project/task") def test_duplicate_successor_is_a_tree_not_a_chain(tmp_path): @@ -608,16 +639,16 @@ def test_duplicate_successor_is_a_tree_not_a_chain(tmp_path): _assert_drift(root, "R-20260901-01.md: has 2 successors (R-20260901-02, R-20260901-03) — a chain, not a tree") -def test_superseded_ruling_as_phase_head(tmp_path): +def test_superseded_ruling_as_task_head(tmp_path): root = _copy(tmp_path) _edit(root, P["08_accepted"], "Ruling: R-20260901-02\n", "Ruling: R-20260901-01\n") _assert_drift(root, "08_accepted.md: Ruling: R-20260901-01 is not a chain head (superseded by R-20260901-02)") -def test_phase_ruling_must_name_that_phase(tmp_path): +def test_task_ruling_must_name_that_task(tmp_path): root = _copy(tmp_path) _edit(root, P["09_rerun"], "Ruling: R-20260901-04\n", "Ruling: R-20260901-05\n") - _assert_drift(root, "09_rerun.md: Ruling: R-20260901-05 names phases/example/10_dropped.md, not this phase", + _assert_drift(root, "09_rerun.md: Ruling: R-20260901-05 names tasks/example/10_dropped.md, not this task", "09_rerun.md: Ruling: R-20260901-05 verb 'drop' does not fit State: rerun") @@ -639,16 +670,16 @@ def test_ruling_id_filename_title_and_directory(tmp_path): "rulings/2026/stray.md: not a ruling path") -def test_ruling_runs_subset_phase_path_verb_and_batch(tmp_path): +def test_ruling_runs_subset_task_path_verb_and_batch(tmp_path): root = _copy(tmp_path) _edit(root, R + "R-20260901-03.md", "Runs: 342091, 342102\n", "Runs: 342091, 342999\n") _edit(root, R + "R-20260901-03.md", "Batch: 2026-09-01-pm\n", "Batch: 2026-09-01-am\n") _edit(root, R + "R-20260901-04.md", "Ruling: rerun\n", "Ruling: redo\n") - _edit(root, R + "R-20260901-05.md", "Phase: phases/example/10_dropped.md\n", "Phase: phases/example/11.md\n") - _assert_drift(root, "R-20260901-03.md: Runs: {342999} not in the phase's runs", + _edit(root, R + "R-20260901-05.md", "Task: tasks/example/10_dropped.md\n", "Task: tasks/example/11.md\n") + _assert_drift(root, "R-20260901-03.md: Runs: {342999} not in the task's runs", "R-20260901-03.md: Batch: 2026-09-01-am names no batches/2026-09-01-am.md", "R-20260901-04.md: Ruling: 'redo' is not a ruling verb", - "R-20260901-05.md: Phase: phases/example/11.md does not resolve to a phase file") + "R-20260901-05.md: Task: tasks/example/11.md does not resolve to a task file") def test_ruling_follow_up_uses_the_gate_grammar(tmp_path): @@ -657,7 +688,7 @@ def test_ruling_follow_up_uses_the_gate_grammar(tmp_path): _assert_drift(root, "R-20260901-02.md: Follow-ups: unrecognised ref 'PyAutoLabs/PyAutoLens#901'") -def test_two_chains_of_one_on_a_phase_are_fine(tmp_path): +def test_two_chains_of_one_on_a_task_are_fine(tmp_path): """leave-to-finish then accept need not chain; Supersedes: replaces, never sequences.""" root = _copy(tmp_path) _move(root, P["05_running_array"], "pulled", "--partial") @@ -678,7 +709,7 @@ def test_planned_to_gated_or_ready_by_gates(tmp_path): r = _move(root, P["01_scope"], "gated") assert r.returncode == 1 and "Gates: is empty — planned → ready" in r.stderr r = _move(root, P["01_scope"], "ready") - assert r.returncode == 0 and r.stdout.strip() == "phases/example/01_scope.md: planned → ready" + assert r.returncode == 0 and r.stdout.strip() == "tasks/example/01_scope.md: planned → ready" assert _fields(root, P["01_scope"])["State"] == "ready" _edit(root, P["01_scope"], "State: ready\nGates:\n", "State: planned\nGates: PyAutoLens#1\n") r = _move(root, P["01_scope"], "ready") @@ -689,7 +720,7 @@ def test_planned_to_gated_or_ready_by_gates(tmp_path): def test_gated_to_ready_is_a_plain_move(tmp_path): """Gate grading is retired: the human reads `gates`, judges the refs and - moves the phase. No flag, and nothing written but `State:`.""" + moves the task. No flag, and nothing written but `State:`.""" root = _copy(tmp_path) before = (root / P["02_gated_on_dev"]).read_text() r = _move(root, P["02_gated_on_dev"], "ready") @@ -748,7 +779,7 @@ def test_run_self_loop_refuses_overlap_and_dangling_after(tmp_path): r = _move(root, P["05_running_array"], "running", "--run", "342091_[9-10]") assert r.returncode == 1 and "overlaps" in r.stderr r = _move(root, P["05_running_array"], "running", "--run", "342200", "--after", "999") - assert r.returncode == 1 and "names no run of this phase" in r.stderr + assert r.returncode == 1 and "names no run of this task" in r.stderr r = _move(root, P["03_ready_cleared"], "ready", "--run", "1") assert r.returncode == 1 and "already ready" in r.stderr @@ -766,7 +797,7 @@ def test_submitted_to_running_and_back_to_ready(tmp_path): f = _fields(root, P["04_submitted_override"]) assert f["State"] == "ready" and f["Reset"] == "both waves died on node failure" assert _problems(root) == [] - # a phase whose runs all succeeded has nothing to reset from + # a task whose runs all succeeded has nothing to reset from _edit(root, P["03_ready_cleared"], "State: ready\n", "State: running\n") _edit(root, P["03_ready_cleared"], "## Runs\n\n", "## Runs\n\n- 1: done — gpu — submitted 2026-09-01 — wall 1:00\n") _edit(root, P["03_ready_cleared"], "Runs:\n", "Runs: 1\n") if "Runs:" in (root / P["03_ready_cleared"]).read_text() \ @@ -793,10 +824,10 @@ def test_running_to_pulled_writes_pulled_to(tmp_path): _edit(root, P["04_submitted_override"], "- 342010: submitted", "- 342010: done") r = _move(root, P["04_submitted_override"], "pulled") assert r.returncode == 1 and "--pulled-to" in r.stderr - r = _move(root, P["04_submitted_override"], "pulled", "--pulled-to", "/mnt/c/out/phase_04") + r = _move(root, P["04_submitted_override"], "pulled", "--pulled-to", "/mnt/c/out/task_04") assert r.returncode == 0, r.stderr text = (root / P["04_submitted_override"]).read_text() - assert "- 342010: done — gpu — submitted 2026-09-01 — wall 0:00\n pulled_to: /mnt/c/out/phase_04\n" in text + assert "- 342010: done — gpu — submitted 2026-09-01 — wall 0:00\n pulled_to: /mnt/c/out/task_04\n" in text assert "- 342001: failed — gpu — submitted 2026-08-31 — wall 0:00 — node failure before the first step\n- 342010" in text assert _problems(root) == [] @@ -823,16 +854,16 @@ def test_legacy_born_ready_to_pulled(tmp_path): @pytest.mark.parametrize("rel,to,needle", [ - (P["07_awaiting_ruling"], "accepted", "rule <phase> accept"), - (P["07_awaiting_ruling"], "rerun", "rule <phase> rerun"), - (P["07_awaiting_ruling"], "dropped", "rule <phase> drop"), - (P["01_scope"], "dropped", "rule <phase> drop"), + (P["07_awaiting_ruling"], "accepted", "rule <task> accept"), + (P["07_awaiting_ruling"], "rerun", "rule <task> rerun"), + (P["07_awaiting_ruling"], "dropped", "rule <task> drop"), + (P["01_scope"], "dropped", "rule <task> drop"), (P["08_accepted"], "rerun", "--supersedes"), (P["10_dropped"], "ready", "terminal"), (P["04_submitted_override"], "pulled", "no edge submitted → pulled"), (P["03_ready_cleared"], "running", "no edge ready → running"), (P["06_pulled"], "ready", "no edge pulled → ready"), - (P["01_scope"], "flying", "not a phase state"), + (P["01_scope"], "flying", "not a task state"), ]) def test_refused_edges(tmp_path, rel, to, needle): root = _copy(tmp_path) @@ -850,7 +881,7 @@ def _rule(root, rel, verb, body, *extra, today=TODAY): return _run("rule", rel, verb, "--body", str(body), "--today", today, *extra, root=root) -def test_rule_assigns_ids_in_sequence_and_moves_the_phase(tmp_path): +def test_rule_assigns_ids_in_sequence_and_moves_the_task(tmp_path): root = _copy(tmp_path) body = _body(tmp_path) r = _rule(root, P["07_awaiting_ruling"], "accept", body, "--batch", "2026-09-01-pm", @@ -858,12 +889,12 @@ def test_rule_assigns_ids_in_sequence_and_moves_the_phase(tmp_path): assert r.returncode == 0, r.stderr assert r.stdout.strip() == "wrote rulings/2026/09/R-20260902-01.md" rf = _fields(root, "rulings/2026/09/R-20260902-01.md") - assert rf["Phase"] == P["07_awaiting_ruling"] and rf["Ruling"] == "accept" + assert rf["Task"] == P["07_awaiting_ruling"] and rf["Ruling"] == "accept" assert rf["Runs"] == "342110, 342120" and rf["Batch"] == "2026-09-01-pm" assert rf["Review-minutes-actual"] == "4" and rf["Follow-ups"] == "PyAutoLens#902" assert rf["Reviewed-at"].startswith("2026-09-02T") text = (root / "rulings/2026/09/R-20260902-01.md").read_text() - assert text.startswith("# R-20260902-01 — accept example phase 7\n") + assert text.startswith("# R-20260902-01 — accept example 07_awaiting_ruling\n") assert "## Ruling\n\nAccept. The human's words, verbatim.\n\n## Evidence\n\n- " in text pf = _fields(root, P["07_awaiting_ruling"]) assert pf["State"] == "accepted" and pf["Ruling"] == "R-20260902-01" @@ -954,83 +985,106 @@ def test_rule_validates_batch_follow_up_and_body(tmp_path): # --------------------------------------------------------------------------- # # gates # --------------------------------------------------------------------------- # -def test_gates_offline_lists_gated_phases(): +def test_gates_offline_lists_gated_tasks(): r = _run("gates", root=SKELETON) assert r.returncode == 0 assert r.stdout.splitlines() == [ - "gates: 1 phase(s)", - " phases/example/02_gated_on_dev.md: gated — PyAutoArray#431, " + "gates: 1 task(s)", + " tasks/example/02_gated_on_dev.md: gated — PyAutoArray#431, " "https://github.com/PyAutoLabs/PyAutoFit/pull/1436", " PyAutoArray#431 → https://github.com/PyAutoLabs/PyAutoArray/issues/431", " https://github.com/PyAutoLabs/PyAutoFit/pull/1436 → " "https://github.com/PyAutoLabs/PyAutoFit/issues/1436", ] r = _run("gates", root=EMPTY) - assert r.returncode == 0 and r.stdout.strip() == "gates: no gated phase" + assert r.returncode == 0 and r.stdout.strip() == "gates: no gated task" # --------------------------------------------------------------------------- # # new # --------------------------------------------------------------------------- # -def test_new_writes_a_parseable_phase(tmp_path): +def test_new_writes_a_parseable_task(tmp_path): root = _copy(tmp_path) - r = _run("new", "example", "11_next", "--phase", "11", "--gates", "PyAutoLens#1, https://github.com/o/r/pull/2", + r = _run("new", "example", "11_next", "--summary", "Does the next idea hold up", + "--gates", "PyAutoLens#1, https://github.com/o/r/pull/2", "--epic", "example-programme", "--budget", "6:00", "--minutes", "5", "--witness", "a claim", "--today", TODAY, root=root) assert r.returncode == 0, r.stderr - assert r.stdout.strip() == "wrote phases/example/11_next.md" - text = (root / "phases/example/11_next.md").read_text() - assert text.startswith("# Example — phase 11: 11 next\n\nProject: example\nPhase: 11\nState: planned\n") - f = _fields(root, "phases/example/11_next.md") + assert r.stdout.strip() == "wrote tasks/example/11_next.md" + text = (root / "tasks/example/11_next.md").read_text() + assert text.startswith("# Example — 11 next\n\nProject: example\n" + "Summary: Does the next idea hold up\nState: planned\n") + f = _fields(root, "tasks/example/11_next.md") + assert f["Summary"] == "Does the next idea hold up" and "Phase" not in f assert f["Gates"] == "PyAutoLens#1, https://github.com/o/r/pull/2" and "Lane" not in f assert f["Filed"] == TODAY and f["Budget"] == "6:00" and f["Review-minutes"] == "5" - for name in cortex.PHASE_SECTIONS: + for name in cortex.TASK_SECTIONS: assert f"\n## {name}\n" in text assert _problems(root) == [] - assert _move(root, "phases/example/11_next.md", "gated").returncode == 0 + assert _move(root, "tasks/example/11_next.md", "gated").returncode == 0 def test_new_refuses_duplicates_and_unknown_projects(tmp_path): + """The slug is the identity: the only collision left is the path itself. + A second task alongside `03_ready_cleared` is fine — it just needs its own + slug — while re-using that slug is refused.""" root = _copy(tmp_path) - r = _run("new", "example", "03_again", "--phase", "3", root=root) - assert r.returncode == 1 and "phase 3 already exists: phases/example/03_ready_cleared.md" in r.stderr - r = _run("new", "example", "03_ready_cleared", "--phase", "11", root=root) + s = ("--summary", "Does the anchor refit reproduce the published value") + r = _run("new", "example", "03_again", *s, "--today", TODAY, root=root) + assert r.returncode == 0, r.stderr + assert _problems(root) == [] + r = _run("new", "example", "03_ready_cleared", *s, root=root) assert r.returncode == 1 and "already exists" in r.stderr - r = _run("new", "nobody", "x", "--phase", "1", root=root) + r = _run("new", "nobody", "x", *s, root=root) assert r.returncode == 1 and "not a projects.yaml key" in r.stderr - r = _run("new", "example", "x", "--phase", "11", "--gates", "PyAutoLabs/PyAutoLens#1", root=root) + r = _run("new", "example", "x", *s, "--gates", "PyAutoLabs/PyAutoLens#1", root=root) assert r.returncode == 1 and "no owner/Repo#N form" in r.stderr - assert not (root / "phases/example/x.md").exists() + assert not (root / "tasks/example/x.md").exists() + + +def test_new_requires_a_summary_within_the_cap(tmp_path): + """`new` refuses what `check` would reject, before writing anything.""" + root = _copy(tmp_path) + r = _run("new", "example", "11_next", root=root) + assert r.returncode == 2 and "--summary" in r.stderr + r = _run("new", "example", "11_next", "--summary", " ", root=root) + assert r.returncode == 1 and "--summary must say what the task asks" in r.stderr + eleven = " ".join(f"w{i}" for i in range(11)) + r = _run("new", "example", "11_next", "--summary", eleven, root=root) + assert r.returncode == 1 and "--summary is 11 words — at most 10" in r.stderr + assert not (root / "tasks/example/11_next.md").exists() -def test_new_legacy_born_phase_is_ready_and_pullable(tmp_path): +def test_new_legacy_born_task_is_ready_and_pullable(tmp_path): root = _copy(tmp_path) - r = _run("new", "example", "12_legacy", "--phase", "12", "--legacy-run", "300000", + r = _run("new", "example", "12_legacy", "--summary", "Is the quarantined run reusable", + "--legacy-run", "300000", "--legacy-wrong", "300001_[0-3]", "--today", TODAY, root=root) assert r.returncode == 1 and "--where" in r.stderr - r = _run("new", "example", "12_legacy", "--phase", "12", "--legacy-run", "300000", + r = _run("new", "example", "12_legacy", "--summary", "Is the quarantined run reusable", + "--legacy-run", "300000", "--legacy-wrong", "300001_[0-3]", "--where", "/mnt/q", "--witness", "w", "--today", TODAY, root=root) assert r.returncode == 0, r.stderr - f = _fields(root, "phases/example/12_legacy.md") + f = _fields(root, "tasks/example/12_legacy.md") assert f["State"] == "ready" and f["Runs"] == "300000, 300001" - text = (root / "phases/example/12_legacy.md").read_text() + text = (root / "tasks/example/12_legacy.md").read_text() assert "- 300000: legacy — gpu — submitted 2026-09-02 — wall 0:00 — pre-Cortex run, migrated\n where: /mnt/q\n" in text assert "- 300001_[0-3]: legacy_wrong — gpu" in text assert _problems(root) == [] - assert _move(root, "phases/example/12_legacy.md", "pulled").returncode == 0 - text = (root / "phases/example/12_legacy.md").read_text() + assert _move(root, "tasks/example/12_legacy.md", "pulled").returncode == 0 + text = (root / "tasks/example/12_legacy.md").read_text() # the reusable run gets pulled_to: (its where:), the wrong one does not assert " where: /mnt/q\n pulled_to: /mnt/q\n- 300001_[0-3]: legacy_wrong" in text assert text.count("pulled_to:") == 1 assert _problems(root) == [] # legacy_wrong-only: nothing to pull — rule drop is the door - r = _run("new", "example", "14", "--phase", "14", "--legacy-wrong", "2", "--where", "/q", - "--witness", "w", root=root) + r = _run("new", "example", "14", "--summary", "Is the wrong-stack run reusable", + "--legacy-wrong", "2", "--where", "/q", "--witness", "w", root=root) assert r.returncode == 0, r.stderr - r = _move(root, "phases/example/14.md", "pulled") + r = _move(root, "tasks/example/14.md", "pulled") assert r.returncode == 1 and "no legacy run to pull" in r.stderr - r = _run("new", "example", "13", "--phase", "13", "--legacy-run", "1", "--where", "/q", - "--gates", "PyAutoLens#1", root=root) + r = _run("new", "example", "13", "--summary", "Is the gated legacy run reusable", + "--legacy-run", "1", "--where", "/q", "--gates", "PyAutoLens#1", root=root) assert r.returncode == 1 and "cannot be gated" in r.stderr diff --git a/tests/test_ledger_merge.py b/tests/test_ledger_merge.py index c9afd87..56c55d1 100644 --- a/tests/test_ledger_merge.py +++ b/tests/test_ledger_merge.py @@ -29,8 +29,8 @@ def test_ledger_dirs_and_registry_files_are_ledger(): for path in ( - "phases/example/01_scope.md", - "phases/newproject/01_first.md", + "tasks/example/01_scope.md", + "tasks/newproject/01_first.md", "rulings/2026/09/R-20260901-01.md", "batches/2026-09-01-pm.md", "batches/reviews/2026-09-01-pm.md", @@ -47,7 +47,7 @@ def test_every_code_home_needs_a_human(): "scripts/cortex.py", "scripts/ledger_merge.py", "tests/test_cortex.py", - "tests/fixtures/skeleton/phases/example/01_scope.md", + "tests/fixtures/skeleton/tasks/example/01_scope.md", ".github/workflows/ledger_merge.yml", ".github/workflows/cortex_check.yml", ".github/workflows/dashboard_refresh.yml", @@ -75,12 +75,12 @@ def test_unclassified_paths_default_to_deny(): def test_traversal_cannot_smuggle_code_behind_a_ledger_prefix(): - for path in ("phases/../scripts/evil.py", "rulings/../../etc/passwd", ".."): + for path in ("tasks/../scripts/evil.py", "rulings/../../etc/passwd", ".."): assert not ledger_merge.is_ledger_path(path), path def test_dot_paths_are_never_ledger_wherever_they_sit(): - for path in ("phases/.github/workflows/x.yml", "batches/.hidden", ".phases/x.md"): + for path in ("tasks/.github/workflows/x.yml", "batches/.hidden", ".tasks/x.md"): assert not ledger_merge.is_ledger_path(path), path @@ -88,7 +88,7 @@ def test_inert_assets_ride_along_but_collectable_tests_do_not(): assert ledger_merge.is_ledger_path("batches/reviews/2026-09-01-pm.md") for path in ( "batches/conftest.py", - "phases/example/test_thing.py", + "tasks/example/test_thing.py", "rulings/2026/09/thing_test.py", ): assert not ledger_merge.is_ledger_path(path), path @@ -105,7 +105,7 @@ def test_doctrine_under_a_ledger_dir_needs_a_human(): "batches/reviews/AGENTS.md", # by name, not by that fixed list — a doctrine file in a dir nobody has # created yet is code on the day it appears - "phases/example/AGENTS.md", + "tasks/example/AGENTS.md", ): assert not ledger_merge.is_ledger_path(path), path # ordinary entries beside them still merge @@ -114,9 +114,9 @@ def test_doctrine_under_a_ledger_dir_needs_a_human(): def test_every_tracked_file_under_a_ledger_dir_gets_the_right_verdict(): - """The witness on the live tree: every tracked path under phases/, rulings/ + """The witness on the live tree: every tracked path under tasks/, rulings/ and batches/ is ledger unless it is doctrine.""" - out = subprocess.run(["git", "ls-files", "phases", "rulings", "batches"], cwd=REPO, + out = subprocess.run(["git", "ls-files", "tasks", "rulings", "batches"], cwd=REPO, capture_output=True, text=True, check=True).stdout.split() assert out, "no ledger-dir paths tracked" for path in out: @@ -126,9 +126,9 @@ def test_every_tracked_file_under_a_ledger_dir_gets_the_right_verdict(): def test_classify_splits_and_dedupes_preserving_order(): ledger, blocked = ledger_merge.classify( - ["checkin.yaml", "scripts/x.py", "checkin.yaml", "phases/a/b.md", "", " "] + ["checkin.yaml", "scripts/x.py", "checkin.yaml", "tasks/a/b.md", "", " "] ) - assert ledger == ["checkin.yaml", "phases/a/b.md"] + assert ledger == ["checkin.yaml", "tasks/a/b.md"] assert blocked == ["scripts/x.py"] @@ -144,19 +144,19 @@ def test_added_ledger_of_record_is_ledger_but_any_other_status_is_code(): assert not ledger_merge.is_append_only_violation("A", rel), rel for status in ("M", "D", "R100", "R", "T", "C75", "U"): assert ledger_merge.is_append_only_violation(status, rel), (rel, status) - # a modified phase file is still ledger — phases move, that is their job - assert not ledger_merge.is_append_only_violation("M", "phases/example/01_scope.md") + # a modified task file is still ledger — tasks move, that is their job + assert not ledger_merge.is_append_only_violation("M", "tasks/example/01_scope.md") def test_classify_entries_blocks_a_modified_ruling_or_record(): ledger, blocked = ledger_merge.classify_entries([ ("A", "rulings/2026/09/R-20260901-06.md"), ("M", "rulings/2026/09/R-20260901-01.md"), - ("M", "phases/example/08_accepted.md"), + ("M", "tasks/example/08_accepted.md"), ("D", "batches/2026-09-01-pm.md"), ("A", "scripts/new.py"), ]) - assert ledger == ["rulings/2026/09/R-20260901-06.md", "phases/example/08_accepted.md"] + assert ledger == ["rulings/2026/09/R-20260901-06.md", "tasks/example/08_accepted.md"] assert blocked == ["rulings/2026/09/R-20260901-01.md", "batches/2026-09-01-pm.md", "scripts/new.py"] @@ -172,9 +172,9 @@ def _repo(tmp_path): _git(repo, "config", "user.email", "t@example.com") _git(repo, "config", "user.name", "t") (repo / "rulings" / "2026" / "09").mkdir(parents=True) - (repo / "phases" / "p").mkdir(parents=True) + (repo / "tasks" / "p").mkdir(parents=True) (repo / "rulings/2026/09/R-20260901-01.md").write_text("# R-20260901-01\n\nRuling: accept\n") - (repo / "phases/p/01_a.md").write_text("# a\n\nState: ready\n") + (repo / "tasks/p/01_a.md").write_text("# a\n\nState: ready\n") (repo / "checkin.yaml").write_text("refreshed: 2026-09-04T12:00Z\n") _git(repo, "add", "-A") _git(repo, "commit", "-q", "-m", "base") @@ -185,17 +185,17 @@ def _repo(tmp_path): def test_changed_entries_reads_name_status_from_git(tmp_path): repo = _repo(tmp_path) (repo / "rulings/2026/09/R-20260901-02.md").write_text("# R-20260901-02\n") - (repo / "phases/p/01_a.md").write_text("# a\n\nState: submitted\n") + (repo / "tasks/p/01_a.md").write_text("# a\n\nState: submitted\n") (repo / "rulings/2026/09/R-20260901-01.md").write_text("# R-20260901-01\n\nRuling: drop\n") _git(repo, "add", "-A") _git(repo, "commit", "-q", "-m", "work") entries = ledger_merge.changed_entries("main", cwd=repo) assert sorted(entries) == [("A", "rulings/2026/09/R-20260901-02.md"), - ("M", "phases/p/01_a.md"), - ("M", "rulings/2026/09/R-20260901-01.md")] + ("M", "rulings/2026/09/R-20260901-01.md"), + ("M", "tasks/p/01_a.md")] ledger, blocked = ledger_merge.classify_entries(entries) assert blocked == ["rulings/2026/09/R-20260901-01.md"] - assert set(ledger) == {"rulings/2026/09/R-20260901-02.md", "phases/p/01_a.md"} + assert set(ledger) == {"rulings/2026/09/R-20260901-02.md", "tasks/p/01_a.md"} def test_a_renamed_or_deleted_ruling_is_code_but_added_ones_merge(tmp_path): @@ -206,11 +206,11 @@ def test_a_renamed_or_deleted_ruling_is_code_but_added_ones_merge(tmp_path): _, blocked = ledger_merge.classify_entries(entries) assert "rulings/2026/09/R-20260901-01.md" in blocked assert "rulings/2026/09/R-20260901-03.md" in blocked - # a branch that only ADDS rulings and moves phases is ledger-only + # a branch that only ADDS rulings and moves tasks is ledger-only _git(repo, "checkout", "-q", "main") _git(repo, "checkout", "-q", "-b", "claude/clean") (repo / "rulings/2026/09/R-20260901-04.md").write_text("# R-20260901-04\n") - (repo / "phases/p/01_a.md").write_text("# a\n\nState: accepted\n") + (repo / "tasks/p/01_a.md").write_text("# a\n\nState: accepted\n") (repo / "checkin.yaml").write_text("refreshed: 2026-09-04T13:00Z\n") _git(repo, "add", "-A") _git(repo, "commit", "-q", "-m", "clean") @@ -228,7 +228,7 @@ def _run(*args, stdin=""): def test_cli_exit_codes_separate_ledger_from_code(): - assert _run("checkin.yaml", "phases/a/b.md", "rulings/2026/09/R-20260901-01.md").returncode == 0 + assert _run("checkin.yaml", "tasks/a/b.md", "rulings/2026/09/R-20260901-01.md").returncode == 0 result = _run("checkin.yaml", "scripts/cortex.py") assert result.returncode == 1 assert "scripts/cortex.py" in result.stdout @@ -294,12 +294,12 @@ def test_the_page_workflow_spells_the_conductor_as_implemented(): def test_no_scheduled_job_mutates_the_ledger(): - """Gate grading was the one cron that wrote phase headers; it was retired + """Gate grading was the one cron that wrote task headers; it was retired on 2026-09-03. Nothing schedules a ledger write any more.""" workflows = (REPO / ".github" / "workflows") for path in sorted(workflows.glob("*.yml")): text = path.read_text() if "schedule:" not in text: continue - assert "git add phases" not in text, path.name + assert "git add tasks" not in text, path.name assert "git add rulings" not in text, path.name From 8fe4f494fc2700f7a70365990b38b8b1b20581de Mon Sep 17 00:00:00 2001 From: Jammy2211 <JNightingale2211@gmail.com> Date: Mon, 7 Sep 2026 14:42:19 -0400 Subject: [PATCH 2/3] cortex: re-render the board on the task vocabulary Refs #20. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EpM4gQikgEtCqpBJ3hzgct --- dashboard.html | 131 +++++++++++++++++++--------------------- dashboard.md | 160 +++++++++++++++++++++---------------------------- 2 files changed, 128 insertions(+), 163 deletions(-) diff --git a/dashboard.html b/dashboard.html index 6676329..4400104 100644 --- a/dashboard.html +++ b/dashboard.html @@ -8,12 +8,16 @@ <style>:root{color-scheme:light dark;--bg:#fff;--fg:#1f2328;--muted:#59636e; --line:#d8dee4;--btn:#f6f8fa;--ok:#1a7f37;--warn:#9a6700;--bad:#d1242f; --accent:#a5177d;--tint:#a5177d14;--edge:#a5177d3d; + /* the ink that READS on a solid --accent fill: the accent is a dark ink on + the light scheme, so white sits on it; on the dark scheme the accent is + the bright end of the pair and the page's own ground is what reads. */ + --accent-ink:#fff; --hero-lift:#340a2a;--hero-base:#000000;--glow:#ff4fc3; --glow2:#ff4fc3} @media(prefers-color-scheme:dark){:root{--bg:#0d1117;--fg:#f0f6fc; --muted:#9198a1;--line:#2c333c;--btn:#151b23;--ok:#3fb950;--warn:#d29922; --bad:#f85149;--accent:#ff7ad9;--tint:#ff7ad91f; - --edge:#ff7ad947}} + --edge:#ff7ad947;--accent-ink:#0d1117}} *{box-sizing:border-box} /* Wrapping is the page DEFAULT, not a per-component opt-in. These boards are read on phones, and every one of them prints run URLs, dotted test ids and @@ -219,100 +223,87 @@ a.go:hover{opacity:.9;text-decoration:none} .mdsrc{font-size:.85em} .boards a[data-organ="brain"]{color:#2159c9}@media(prefers-color-scheme:dark){.boards a[data-organ="brain"]{color:#6f9dff}}.boards a[data-organ="mind"]{color:#0a7d72}@media(prefers-color-scheme:dark){.boards a[data-organ="mind"]{color:#2ee6cf}}.boards a[data-organ="cortex"]{color:#a5177d}@media(prefers-color-scheme:dark){.boards a[data-organ="cortex"]{color:#ff7ad9}}.boards a[data-organ="memory"]{color:#6b34d6}@media(prefers-color-scheme:dark){.boards a[data-organ="memory"]{color:#b37eff}}.boards a[data-organ="heart"]{color:#b50f1a}@media(prefers-color-scheme:dark){.boards a[data-organ="heart"]{color:#ff6b73}}.boards a[data-organ="hands"]{color:#9a5400}@media(prefers-color-scheme:dark){.boards a[data-organ="hands"]{color:#ffa733}}.boards a[data-organ="organism"]{color:#0b6fa4}@media(prefers-color-scheme:dark){.boards a[data-organ="organism"]{color:#5ec7f5}} -.fresh{padding:.1rem .9rem;margin:1.2rem 0;background:var(--tint)}.fresh p{margin:.5rem 0}table.map{width:100%;border-collapse:collapse;font-size:.95em}table.map th{text-align:left;color:var(--muted);font-weight:600}table.map td,table.map th{border-bottom:1px solid var(--line);padding:.4rem .5rem .4rem 0;vertical-align:top}.checkin{display:flex;gap:.9rem;align-items:center;padding:.9rem 1.2rem;border:2px solid var(--line);border-radius:.6rem;margin:.2rem 0 1.4rem}.checkin .mark{font-size:2rem;line-height:1}.checkin .label{display:block;font-size:.75rem;letter-spacing:.06em;text-transform:uppercase;color:var(--muted)}.checkin time{font-size:1.6rem;font-weight:700;font-variant-numeric:tabular-nums;display:block}.checkin .note{font-size:.85rem;color:var(--muted)}.checkin.fresh-ok{border-color:var(--ok);background:color-mix(in srgb,var(--ok) 12%,transparent)}.checkin.fresh-ok .mark{color:var(--ok)}.checkin.fresh-bad{border-color:var(--bad);background:color-mix(in srgb,var(--bad) 12%,transparent)}.checkin.fresh-bad .mark{color:var(--bad)}.project h3{font-size:1.35rem;font-weight:700;padding-bottom:.2rem;border-bottom:2px solid var(--accent);letter-spacing:.01em;margin-top:1.6rem}.paths{margin:.2rem 0}.paths b{display:inline-block;min-width:3.6rem}.stale{color:var(--bad);font-weight:600}</style> +.fresh{padding:.1rem .9rem;margin:1.2rem 0;background:var(--tint)}.fresh p{margin:.5rem 0}table.map{width:100%;border-collapse:collapse;font-size:.95em}table.map th{text-align:left;color:var(--muted);font-weight:600}table.map td,table.map th{border-bottom:1px solid var(--line);padding:.4rem .5rem .4rem 0;vertical-align:top}.checkin{display:flex;gap:.9rem;align-items:center;padding:.9rem 1.2rem;border:2px solid var(--line);border-radius:.6rem;margin:.2rem 0 1.4rem}.checkin .mark{font-size:2rem;line-height:1}.checkin .label{display:block;font-size:.75rem;letter-spacing:.06em;text-transform:uppercase;color:var(--muted)}.checkin time{font-size:1.6rem;font-weight:700;font-variant-numeric:tabular-nums;display:block}.checkin .note{font-size:.85rem;color:var(--muted)}.checkin.fresh-ok{border-color:var(--ok);background:color-mix(in srgb,var(--ok) 12%,transparent)}.checkin.fresh-ok .mark{color:var(--ok)}.checkin.fresh-bad{border-color:var(--bad);background:color-mix(in srgb,var(--bad) 12%,transparent)}.checkin.fresh-bad .mark{color:var(--bad)}.project h3{font-size:1.35rem;font-weight:700;padding-bottom:.2rem;border-bottom:2px solid var(--accent);letter-spacing:.01em;margin-top:1.6rem}.paths{margin:.2rem 0}.paths b{display:inline-block;min-width:3.6rem}.pathchip{display:inline-block;background:var(--accent);color:var(--accent-ink);font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:.88em;padding:.12em .5em;border-radius:.3rem;overflow-wrap:anywhere}.stale{color:var(--bad);font-weight:600}</style> </head> <body> -<header class="hero"><span class="orb"><svg class="mark" viewBox="0 0 48 48" fill="none" stroke="currentColor" stroke-width="1.3" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><circle cx="24" cy="24" r="20.4"/><path d="M11.8,18.6 C14.4,14.8 17.8,14.4 20.1,17.1 C22.4,19.8 25.8,20.2 28.5,17.9 C31.2,15.6 34.4,16.2 36.1,19.1"/><path d="M11.6,24.6 C14.2,20.8 17.6,20.4 19.9,23.1 C22.2,25.8 25.6,26.2 28.3,23.9 C31.0,21.6 34.2,22.2 35.9,25.1"/><path d="M12.8,30.6 C15.3,27.2 18.4,26.8 20.5,29.4 C22.6,32.0 25.7,32.4 28.2,30.2"/><circle cx="32.6" cy="33.0" r="3.8"/><g fill="currentColor" stroke="none"><circle cx="32.6" cy="33.0" r="1.5"/><circle cx="11.7" cy="18.6" r="1.2"/><circle cx="11.5" cy="24.6" r="1.2"/></g></svg></span><h1>PyAuto<b>Cortex</b><span class="kind">Dashboard</span></h1><div class="rule"></div><p class="tag">Question. Run. Rule.</p></header><p class="lede">Every science phase the Cortex is holding. Tap a phase's 📋 and its next command is on your clipboard. The verdict is always yours — this page hands you the command, never the ruling.</p> -<ul class="stats"><li><b>0</b><span>Awaiting ruling</span></li><li><b>6</b><span>Running / submitted</span></li><li><b>0</b><span>Ready</span></li><li><b>1</b><span>Gated</span></li><li><b>28</b><span>Recent rulings</span></li></ul> +<header class="hero"><span class="orb"><svg class="mark" viewBox="0 0 48 48" fill="none" stroke="currentColor" stroke-width="1.3" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><circle cx="24" cy="24" r="20.4"/><path d="M11.8,18.6 C14.4,14.8 17.8,14.4 20.1,17.1 C22.4,19.8 25.8,20.2 28.5,17.9 C31.2,15.6 34.4,16.2 36.1,19.1"/><path d="M11.6,24.6 C14.2,20.8 17.6,20.4 19.9,23.1 C22.2,25.8 25.6,26.2 28.3,23.9 C31.0,21.6 34.2,22.2 35.9,25.1"/><path d="M12.8,30.6 C15.3,27.2 18.4,26.8 20.5,29.4 C22.6,32.0 25.7,32.4 28.2,30.2"/><circle cx="32.6" cy="33.0" r="3.8"/><g fill="currentColor" stroke="none"><circle cx="32.6" cy="33.0" r="1.5"/><circle cx="11.7" cy="18.6" r="1.2"/><circle cx="11.5" cy="24.6" r="1.2"/></g></svg></span><h1>PyAuto<b>Cortex</b><span class="kind">Dashboard</span></h1><div class="rule"></div><p class="tag">Question. Run. Rule.</p></header><p class="lede">Every science task the Cortex is holding, one line each under its project. Tap a task's 📋 and its next command is on your clipboard. The verdict is always yours — this page hands you the command, never the ruling.</p> +<ul class="stats"><li><b>0</b><span>Awaiting ruling</span></li><li><b>28</b><span>Recent rulings</span></li></ul> <div class="fresh"><p class="muted">Last updated 2026-09-07.</p></div> <p class="muted mdsrc"><a href="https://github.com/PyAutoLabs/PyAutoCortex/blob/main/dashboard.md">markdown version</a> · <a href="https://github.com/PyAutoLabs/PyAutoCortex/blob/main/README.md">GitHub Page</a></p> -<div class="task"><button class="copy" data-cmd="/cortex — check in on every active science project since the last check-in (2026-09-07T15:28Z): pull each project through its sync CLI, score every live run against its pre-registered witness, move what came back, re-render and push the board, then read me the by-project summary with the prompt each phase needs next." aria-label="Copy the Claude command">📋</button><p>check in since last time</p></div> +<div class="task"><button class="copy" data-cmd="/cortex — check in on every active science project since the last check-in (2026-09-07T15:28Z): pull each project through its sync CLI, score every live run against its pre-registered witness, move what came back, re-render and push the board, then read me the by-project summary with the prompt each task needs next." aria-label="Copy the Claude command">📋</button><p>check in since last time</p></div> <div class="checkin" id="checkin-box"><span class="mark" id="checkin-mark"></span><div><span class="label">Last check-in</span><time id="checkin" datetime="2026-09-07T15:28Z">2026-09-07T15:28Z</time><span class="note" id="checkin-note"></span></div></div> <a id="summary"></a><h2>Summary</h2> <p class="muted">One row per active project — the next thing, what it is holding, and when it last ruled.</p> <table class="map"> -<tr><th>Project</th><th>Next phase</th><th>Awaiting</th><th>Running</th><th>Ready</th><th>Last ruling</th></tr> -<tr><td><b>euclid_dr1_prelim</b></td><td>4 submitted</td><td>0</td><td>1</td><td>0</td><td>-</td></tr> -<tr><td><b>inference_programme</b></td><td>21 gated</td><td>0</td><td>0</td><td>0</td><td>2026-09-04</td></tr> -<tr><td><b>subhalo_validation</b></td><td>2 submitted</td><td>0</td><td>5</td><td>0</td><td>2026-09-07</td></tr> +<tr><th>Project</th><th>Next task</th><th>Awaiting</th><th>Running</th><th>Last ruling</th></tr> +<tr><td><b>euclid_dr1_prelim</b></td><td>Ten real DR1 lenses fitted from the pipeline repo alone</td><td>0</td><td>1</td><td>-</td></tr> +<tr><td><b>inference_programme</b></td><td>What Nautilus achieves on mass_pix at f1e8 and f1e5</td><td>0</td><td>0</td><td>2026-09-04</td></tr> +<tr><td><b>subhalo_validation</b></td><td>Does rectangular_adapt false-detect a subhalo on the clumpy source</td><td>0</td><td>5</td><td>2026-09-07</td></tr> </table> <h3>Nothing open</h3> <table class="map"> -<tr><th>Project</th><th>Status</th><th>Phases</th><th>Retire</th></tr> -<tr><td>concr</td><td>dormant</td><td>none</td><td><button class="copy text" data-cmd="/cortex — retire the science project concr: confirm it is holding nothing live, then in PyAutoCortex run `python3 scripts/cortex.py retire concr --why "<one line on why>"`, `python3 scripts/cortex.py check`, `pyauto-brain cortex dashboard --apply`, and push the ledger. The row, its phases and its rulings all stay — only the status and the note change." aria-label="Copy the retire prompt">📋 retire</button></td></tr> -<tr><td>cowls_diana</td><td>dormant</td><td>none</td><td><button class="copy text" data-cmd="/cortex — retire the science project cowls_diana: confirm it is holding nothing live, then in PyAutoCortex run `python3 scripts/cortex.py retire cowls_diana --why "<one line on why>"`, `python3 scripts/cortex.py check`, `pyauto-brain cortex dashboard --apply`, and push the ledger. The row, its phases and its rulings all stay — only the status and the note change." aria-label="Copy the retire prompt">📋 retire</button></td></tr> -<tr><td>euclid</td><td>dormant</td><td>planned 3</td><td><button class="copy text" data-cmd="/cortex — retire the science project euclid: confirm it is holding nothing live, then in PyAutoCortex run `python3 scripts/cortex.py retire euclid --why "<one line on why>"`, `python3 scripts/cortex.py check`, `pyauto-brain cortex dashboard --apply`, and push the ledger. The row, its phases and its rulings all stay — only the status and the note change." aria-label="Copy the retire prompt">📋 retire</button></td></tr> -<tr><td>ic50_workspace</td><td>dormant</td><td>planned 1</td><td><button class="copy text" data-cmd="/cortex — retire the science project ic50_workspace: confirm it is holding nothing live, then in PyAutoCortex run `python3 scripts/cortex.py retire ic50_workspace --why "<one line on why>"`, `python3 scripts/cortex.py check`, `pyauto-brain cortex dashboard --apply`, and push the ledger. The row, its phases and its rulings all stay — only the status and the note change." aria-label="Copy the retire prompt">📋 retire</button></td></tr> -<tr><td>pj011646</td><td>dormant</td><td>none</td><td><button class="copy text" data-cmd="/cortex — retire the science project pj011646: confirm it is holding nothing live, then in PyAutoCortex run `python3 scripts/cortex.py retire pj011646 --why "<one line on why>"`, `python3 scripts/cortex.py check`, `pyauto-brain cortex dashboard --apply`, and push the ledger. The row, its phases and its rulings all stay — only the status and the note change." aria-label="Copy the retire prompt">📋 retire</button></td></tr> -<tr><td>profiling</td><td>dormant</td><td>none</td><td><button class="copy text" data-cmd="/cortex — retire the science project profiling: confirm it is holding nothing live, then in PyAutoCortex run `python3 scripts/cortex.py retire profiling --why "<one line on why>"`, `python3 scripts/cortex.py check`, `pyauto-brain cortex dashboard --apply`, and push the ledger. The row, its phases and its rulings all stay — only the status and the note change." aria-label="Copy the retire prompt">📋 retire</button></td></tr> -<tr><td>slope_hierarchy</td><td>dormant</td><td>planned 2</td><td><button class="copy text" data-cmd="/cortex — retire the science project slope_hierarchy: confirm it is holding nothing live, then in PyAutoCortex run `python3 scripts/cortex.py retire slope_hierarchy --why "<one line on why>"`, `python3 scripts/cortex.py check`, `pyauto-brain cortex dashboard --apply`, and push the ledger. The row, its phases and its rulings all stay — only the status and the note change." aria-label="Copy the retire prompt">📋 retire</button></td></tr> -<tr><td>subhalo_simulations</td><td>dormant</td><td>none</td><td><button class="copy text" data-cmd="/cortex — retire the science project subhalo_simulations: confirm it is holding nothing live, then in PyAutoCortex run `python3 scripts/cortex.py retire subhalo_simulations --why "<one line on why>"`, `python3 scripts/cortex.py check`, `pyauto-brain cortex dashboard --apply`, and push the ledger. The row, its phases and its rulings all stay — only the status and the note change." aria-label="Copy the retire prompt">📋 retire</button></td></tr> +<tr><th>Project</th><th>Status</th><th>Tasks</th><th>Retire</th></tr> +<tr><td>concr</td><td>dormant</td><td>none</td><td><button class="copy text" data-cmd="/cortex — retire the science project concr: confirm it is holding nothing live, then in PyAutoCortex run `python3 scripts/cortex.py retire concr --why "<one line on why>"`, `python3 scripts/cortex.py check`, `pyauto-brain cortex dashboard --apply`, and push the ledger. The row, its tasks and its rulings all stay — only the status and the note change." aria-label="Copy the retire prompt">📋 retire</button></td></tr> +<tr><td>cowls_diana</td><td>dormant</td><td>none</td><td><button class="copy text" data-cmd="/cortex — retire the science project cowls_diana: confirm it is holding nothing live, then in PyAutoCortex run `python3 scripts/cortex.py retire cowls_diana --why "<one line on why>"`, `python3 scripts/cortex.py check`, `pyauto-brain cortex dashboard --apply`, and push the ledger. The row, its tasks and its rulings all stay — only the status and the note change." aria-label="Copy the retire prompt">📋 retire</button></td></tr> +<tr><td>euclid</td><td>dormant</td><td>planned 3</td><td><button class="copy text" data-cmd="/cortex — retire the science project euclid: confirm it is holding nothing live, then in PyAutoCortex run `python3 scripts/cortex.py retire euclid --why "<one line on why>"`, `python3 scripts/cortex.py check`, `pyauto-brain cortex dashboard --apply`, and push the ledger. The row, its tasks and its rulings all stay — only the status and the note change." aria-label="Copy the retire prompt">📋 retire</button></td></tr> +<tr><td>ic50_workspace</td><td>dormant</td><td>planned 1</td><td><button class="copy text" data-cmd="/cortex — retire the science project ic50_workspace: confirm it is holding nothing live, then in PyAutoCortex run `python3 scripts/cortex.py retire ic50_workspace --why "<one line on why>"`, `python3 scripts/cortex.py check`, `pyauto-brain cortex dashboard --apply`, and push the ledger. The row, its tasks and its rulings all stay — only the status and the note change." aria-label="Copy the retire prompt">📋 retire</button></td></tr> +<tr><td>pj011646</td><td>dormant</td><td>none</td><td><button class="copy text" data-cmd="/cortex — retire the science project pj011646: confirm it is holding nothing live, then in PyAutoCortex run `python3 scripts/cortex.py retire pj011646 --why "<one line on why>"`, `python3 scripts/cortex.py check`, `pyauto-brain cortex dashboard --apply`, and push the ledger. The row, its tasks and its rulings all stay — only the status and the note change." aria-label="Copy the retire prompt">📋 retire</button></td></tr> +<tr><td>profiling</td><td>dormant</td><td>none</td><td><button class="copy text" data-cmd="/cortex — retire the science project profiling: confirm it is holding nothing live, then in PyAutoCortex run `python3 scripts/cortex.py retire profiling --why "<one line on why>"`, `python3 scripts/cortex.py check`, `pyauto-brain cortex dashboard --apply`, and push the ledger. The row, its tasks and its rulings all stay — only the status and the note change." aria-label="Copy the retire prompt">📋 retire</button></td></tr> +<tr><td>slope_hierarchy</td><td>dormant</td><td>planned 2</td><td><button class="copy text" data-cmd="/cortex — retire the science project slope_hierarchy: confirm it is holding nothing live, then in PyAutoCortex run `python3 scripts/cortex.py retire slope_hierarchy --why "<one line on why>"`, `python3 scripts/cortex.py check`, `pyauto-brain cortex dashboard --apply`, and push the ledger. The row, its tasks and its rulings all stay — only the status and the note change." aria-label="Copy the retire prompt">📋 retire</button></td></tr> +<tr><td>subhalo_simulations</td><td>dormant</td><td>none</td><td><button class="copy text" data-cmd="/cortex — retire the science project subhalo_simulations: confirm it is holding nothing live, then in PyAutoCortex run `python3 scripts/cortex.py retire subhalo_simulations --why "<one line on why>"`, `python3 scripts/cortex.py check`, `pyauto-brain cortex dashboard --apply`, and push the ledger. The row, its tasks and its rulings all stay — only the status and the note change." aria-label="Copy the retire prompt">📋 retire</button></td></tr> </table> <a id="projects"></a><h2>Projects <a class="mdsrc" href="https://github.com/PyAutoLabs/PyAutoCortex/blob/main/projects.yaml">markdown version</a></h2> <section class="project"> <h3>euclid_dr1_prelim</h3> -<p class="paths"><b>Local</b> <code>/mnt/c/Users/Jammy/Science/euclid_dr1_prelim</code></p> -<p class="paths"><b>RAL</b> <code>/mnt/ral/jnightin/euclid_dr1_prelim</code></p> -<p class="muted">active · both partition · phases: submitted 1</p> -<div class="task"><button class="copy" data-cmd="Report where the runs of the PyAutoCortex phase phases/euclid_dr1_prelim/dr1_prelim_10_lens_science_run.md (342301_[0-9], 342314_3) stand." aria-label="Copy the Claude command">📋</button><p><a href="https://github.com/PyAutoLabs/PyAutoCortex/blob/main/phases/euclid_dr1_prelim/dr1_prelim_10_lens_science_run.md">Euclid_dr1_prelim — phase 4: DR1 prelim science run — 10 real Euclid lenses in euclid_dr1_prelim on RAL</a><span class="tags"><span class="pill n">submitted</span></span><span class="facets"> — euclid_dr1_prelim phase 4 · budget 48:00 · 25 review-min · runs 342301_[0-9], 342314_3</span></p></div> +<p class="paths"><b>Local</b> <span class="pathchip">/mnt/c/Users/Jammy/Science/euclid_dr1_prelim</span></p> +<p class="paths"><b>RAL</b> <span class="pathchip">/mnt/ral/jnightin/euclid_dr1_prelim</span></p> +<p class="muted">active · both partition · tasks: submitted 1</p> +<div class="task"><button class="copy" data-cmd="Report where the runs of the PyAutoCortex task tasks/euclid_dr1_prelim/dr1_prelim_10_lens_science_run.md (342301_[0-9], 342314_3) stand." aria-label="Copy the Claude command">📋</button><p><a href="https://github.com/PyAutoLabs/PyAutoCortex/blob/main/tasks/euclid_dr1_prelim/dr1_prelim_10_lens_science_run.md">Ten real DR1 lenses fitted from the pipeline repo alone</a><span class="tags"><span class="pill y">submitted</span></span><span class="facets"> — runs 342301_[0-9], 342314_3 · wall 0:00 of 48:00 (0%)</span></p></div> +<p class="muted"><a href="https://github.com/PyAutoLabs/PyAutoCortex/tree/main/tasks/euclid_dr1_prelim/">tasks/euclid_dr1_prelim/</a></p> </section> <section class="project"> <h3>inference_programme</h3> -<p class="paths"><b>Local</b> <code>/home/jammy/Code/PyAutoLabs/autolens_profiling</code></p> -<p class="paths"><b>Mirror</b> <code>/mnt/c/Users/Jammy/Science/inference_programme</code></p> -<p class="paths"><b>RAL</b> <code>/mnt/ral/jnightin/autolens_profiling</code></p> -<p class="muted">active · both partition · phases: accepted 7 · dropped 11 · gated 1 · planned 4</p> -<div class="task"><button class="copy" data-cmd="python3 scripts/cortex.py gates # then, once they have closed: move phases/inference_programme/nautilus_mass_pix_baseline.md ready -# gates: autolens_profiling#218" aria-label="Copy the Claude command">📋</button><p><a href="https://github.com/PyAutoLabs/PyAutoCortex/blob/main/phases/inference_programme/nautilus_mass_pix_baseline.md">Inference_programme — phase 21: the Nautilus bar on mass_pix — f1e8 (refs convention) and f1e5 (like-for-like)</a><span class="tags"><span class="pill n">gated</span></span><span class="facets"> — inference_programme phase 21 · budget 4:00 · 15 review-min</span></p></div> -<details><summary>4 more open phase(s) · plans and issues</summary><ul> -<li><a href="https://github.com/PyAutoLabs/PyAutoCortex/blob/main/phases/inference_programme/cluster_extended_source_inference.md">Inference_programme — phase 11: cluster extended-source inference — gradient-based fitting building on JAX knowledge</a> <span class="facets">— planned</span></li> -<li><a href="https://github.com/PyAutoLabs/PyAutoCortex/blob/main/phases/inference_programme/legacy_point_output_sweep.md">Inference_programme — phase 19: sweep the point-source families out of the RAL active output/ tree into output/legacy_point/</a> <span class="facets">— planned</span></li> -<li><a href="https://github.com/PyAutoLabs/PyAutoCortex/blob/main/phases/inference_programme/prodigy_mass_pix.md">Inference_programme — phase 22: MultiStartProdigy on mass_pix — does a gradient search beat Nautilus in a SLaM mass[1] shape?</a> <span class="facets">— planned</span></li> -<li><a href="https://github.com/PyAutoLabs/PyAutoCortex/blob/main/phases/inference_programme/likelihood_term_levers.md">Inference_programme — phase 23: likelihood-term levers on mass_pix — slogdet vs cholesky, relative jitter on reg.Adapt</a> <span class="facets">— planned</span></li> -</ul><p><a href="https://github.com/PyAutoLabs/PyAutoCortex/tree/main/phases/inference_programme/">phases/inference_programme/</a> · <a href="https://github.com/PyAutoLabs/autolens_profiling/issues">issues</a></p></details> +<p class="paths"><b>Local</b> <span class="pathchip">/home/jammy/Code/PyAutoLabs/autolens_profiling</span></p> +<p class="paths"><b>Mirror</b> <span class="pathchip">/mnt/c/Users/Jammy/Science/inference_programme</span></p> +<p class="paths"><b>RAL</b> <span class="pathchip">/mnt/ral/jnightin/autolens_profiling</span></p> +<p class="muted">active · both partition · tasks: accepted 7 · dropped 11 · gated 1 · planned 4</p> +<div class="task"><button class="copy" data-cmd="python3 scripts/cortex.py gates # then, once they have closed: move tasks/inference_programme/nautilus_mass_pix_baseline.md ready +# gates: autolens_profiling#218" aria-label="Copy the Claude command">📋</button><p><a href="https://github.com/PyAutoLabs/PyAutoCortex/blob/main/tasks/inference_programme/nautilus_mass_pix_baseline.md">What Nautilus achieves on mass_pix at f1e8 and f1e5</a><span class="tags"><span class="pill n">gated</span></span><span class="facets"> — autolens_profiling#218</span></p></div> +<div class="task"><button class="copy" data-cmd="python3 scripts/cortex.py move tasks/inference_programme/cluster_extended_source_inference.md ready # when the `Ready when:` clause in its `## Question` is met" aria-label="Copy the Claude command">📋</button><p><a href="https://github.com/PyAutoLabs/PyAutoCortex/blob/main/tasks/inference_programme/cluster_extended_source_inference.md">Cluster extended source inference testing gradient based fitting</a><span class="tags"><span class="pill g">planned</span></span></p></div> +<div class="task"><button class="copy" data-cmd="python3 scripts/cortex.py move tasks/inference_programme/legacy_point_output_sweep.md ready # when the `Ready when:` clause in its `## Question` is met" aria-label="Copy the Claude command">📋</button><p><a href="https://github.com/PyAutoLabs/PyAutoCortex/blob/main/tasks/inference_programme/legacy_point_output_sweep.md">Can spent point-source families be swept out of active output</a><span class="tags"><span class="pill g">planned</span></span></p></div> +<div class="task"><button class="copy" data-cmd="python3 scripts/cortex.py move tasks/inference_programme/likelihood_term_levers.md ready # when the `Ready when:` clause in its `## Question` is met" aria-label="Copy the Claude command">📋</button><p><a href="https://github.com/PyAutoLabs/PyAutoCortex/blob/main/tasks/inference_programme/likelihood_term_levers.md">Are likelihood terms behind gradient search failure on mass_pix</a><span class="tags"><span class="pill g">planned</span></span></p></div> +<div class="task"><button class="copy" data-cmd="python3 scripts/cortex.py move tasks/inference_programme/prodigy_mass_pix.md ready # when the `Ready when:` clause in its `## Question` is met" aria-label="Copy the Claude command">📋</button><p><a href="https://github.com/PyAutoLabs/PyAutoCortex/blob/main/tasks/inference_programme/prodigy_mass_pix.md">Does a gradient search beat Nautilus on mass_pix</a><span class="tags"><span class="pill g">planned</span></span></p></div> +<p class="muted"><a href="https://github.com/PyAutoLabs/PyAutoCortex/tree/main/tasks/inference_programme/">tasks/inference_programme/</a> · <a href="https://github.com/PyAutoLabs/autolens_profiling/issues">issues</a></p> </section> <section class="project"> <h3>subhalo_validation</h3> -<p class="paths"><b>Local</b> <code>/mnt/c/Users/Jammy/Science/subhalo_validation</code></p> -<p class="paths"><b>RAL</b> <code>/mnt/ral/jnightin/subhalo_validation</code></p> -<p class="muted">active · ral partition · phases: accepted 1 · running 2 · submitted 3</p> -<div class="task"><button class="copy" data-cmd="cd /mnt/c/Users/Jammy/Science/subhalo_validation && hpc/sync jobs" aria-label="Copy the Claude command">📋</button><p><a href="https://github.com/PyAutoLabs/PyAutoCortex/blob/main/phases/subhalo_validation/delaunay_adapt_split_pl_eff_0.md">Subhalo_validation — phase 2: delaunay_adapt_split on pl_eff_0 — the clumpy-source lens</a><span class="tags"><span class="pill n">submitted</span></span><span class="facets"> — subhalo_validation phase 2 · budget 48:00 · 8 review-min · runs 342027_1, 342231_1, 342234_1, 342299_1</span><span class="facets"> — ⚠️ failed runs: 342234_1</span></p></div> -<details><summary>4 more open phase(s) · plans and issues</summary><ul> -<li><a href="https://github.com/PyAutoLabs/PyAutoCortex/blob/main/phases/subhalo_validation/delaunay_adapt_split_pl_eff_1_outer.md">Subhalo_validation — phase 3: delaunay_adapt_split on pl_eff_1_outer — the outer-component lens, plus the AdaptSplit fix rerun</a> <span class="facets">— submitted</span></li> -<li><a href="https://github.com/PyAutoLabs/PyAutoCortex/blob/main/phases/subhalo_validation/rectangular_adapt_pl_sersic_0.md">Subhalo_validation — phase 4: rectangular_adapt on pl_sersic_0 — the RectangularBilinear comparison</a> <span class="facets">— submitted</span></li> -<li><a href="https://github.com/PyAutoLabs/PyAutoCortex/blob/main/phases/subhalo_validation/rectangular_adapt_pl_eff_0.md">Subhalo_validation — phase 5: rectangular_adapt on pl_eff_0 — the clumpy-source lens on the RectangularBilinear comparison</a> <span class="facets">— running</span></li> -<li><a href="https://github.com/PyAutoLabs/PyAutoCortex/blob/main/phases/subhalo_validation/rectangular_adapt_pl_eff_1_outer.md">Subhalo_validation — phase 6: rectangular_adapt on pl_eff_1_outer — the outer-component lens on the RectangularBilinear comparison</a> <span class="facets">— running</span></li> -</ul><p><a href="https://github.com/PyAutoLabs/PyAutoCortex/tree/main/phases/subhalo_validation/">phases/subhalo_validation/</a> · <a href="https://github.com/PyAutoLabs/subhalo_validation/issues">issues</a></p></details> +<p class="paths"><b>Local</b> <span class="pathchip">/mnt/c/Users/Jammy/Science/subhalo_validation</span></p> +<p class="paths"><b>RAL</b> <span class="pathchip">/mnt/ral/jnightin/subhalo_validation</span></p> +<p class="muted">active · ral partition · tasks: accepted 1 · running 2 · submitted 3</p> +<div class="task"><button class="copy" data-cmd="cd /mnt/c/Users/Jammy/Science/subhalo_validation && hpc/sync jobs" aria-label="Copy the Claude command">📋</button><p><a href="https://github.com/PyAutoLabs/PyAutoCortex/blob/main/tasks/subhalo_validation/rectangular_adapt_pl_eff_0.md">Does rectangular_adapt false-detect a subhalo on the clumpy source</a><span class="tags"><span class="pill r">running</span></span><span class="facets"> — runs 342237_1, 342240_1, 342311_1 · wall 1:52 of 48:00 (4%) · ⚠️ failed runs: 342240_1</span></p></div> +<div class="task"><button class="copy" data-cmd="cd /mnt/c/Users/Jammy/Science/subhalo_validation && hpc/sync jobs" aria-label="Copy the Claude command">📋</button><p><a href="https://github.com/PyAutoLabs/PyAutoCortex/blob/main/tasks/subhalo_validation/rectangular_adapt_pl_eff_1_outer.md">Does rectangular_adapt false-detect a subhalo on the outer-component lens</a><span class="tags"><span class="pill r">running</span></span><span class="facets"> — runs 342237_2, 342240_2, 342311_2 · wall 1:52 of 48:00 (4%) · ⚠️ failed runs: 342240_2</span></p></div> +<div class="task"><button class="copy" data-cmd="cd /mnt/c/Users/Jammy/Science/subhalo_validation && hpc/sync jobs" aria-label="Copy the Claude command">📋</button><p><a href="https://github.com/PyAutoLabs/PyAutoCortex/blob/main/tasks/subhalo_validation/delaunay_adapt_split_pl_eff_0.md">Does delaunay_adapt_split false-detect a subhalo on the clumpy source</a><span class="tags"><span class="pill r">submitted</span></span><span class="facets"> — runs 342027_1, 342231_1, 342234_1, 342299_1 · wall 45:10 of 48:00 (94%) · ⚠️ failed runs: 342234_1</span></p></div> +<div class="task"><button class="copy" data-cmd="cd /mnt/c/Users/Jammy/Science/subhalo_validation && hpc/sync jobs" aria-label="Copy the Claude command">📋</button><p><a href="https://github.com/PyAutoLabs/PyAutoCortex/blob/main/tasks/subhalo_validation/delaunay_adapt_split_pl_eff_1_outer.md">Does delaunay_adapt_split false-detect on the outer-component lens and AdaptSplit fix</a><span class="tags"><span class="pill r">submitted</span></span><span class="facets"> — runs 342027_2, 342093_0, 342231_2, 342234_2, 342299_2 · wall 47:05 of 48:00 (98%) · ⚠️ failed runs: 342234_2</span></p></div> +<div class="task"><button class="copy" data-cmd="cd /mnt/c/Users/Jammy/Science/subhalo_validation && hpc/sync jobs" aria-label="Copy the Claude command">📋</button><p><a href="https://github.com/PyAutoLabs/PyAutoCortex/blob/main/tasks/subhalo_validation/rectangular_adapt_pl_sersic_0.md">Does rectangular_adapt behave like delaunay_adapt_split on the clean null</a><span class="tags"><span class="pill r">submitted</span></span><span class="facets"> — runs 342094_0, 342095_0, 342237_0, 342240_0, 342311_0 · wall 18:01 of 48:00 (38%) · ⚠️ failed runs: 342240_0</span></p></div> +<p class="muted"><a href="https://github.com/PyAutoLabs/PyAutoCortex/tree/main/tasks/subhalo_validation/">tasks/subhalo_validation/</a> · <a href="https://github.com/PyAutoLabs/subhalo_validation/issues">issues</a></p> </section> -<a id="awaiting-ruling"></a><h2>Awaiting ruling <a class="mdsrc" href="https://github.com/PyAutoLabs/PyAutoCortex/tree/main/phases/">markdown version</a></h2><p class="muted">Results are in and nothing is running — the human's verdict is the only thing outstanding. Ordered failures first, then the phases a ruling is required for, then the clean ones.</p> +<a id="awaiting-ruling"></a><h2>Awaiting ruling <a class="mdsrc" href="https://github.com/PyAutoLabs/PyAutoCortex/tree/main/tasks/">markdown version</a></h2><p class="muted">Results are in and nothing is running — the human's verdict is the only thing outstanding. Ordered failures first, then the tasks a ruling is required for, then the clean ones.</p> <p class="muted">(nothing awaiting a ruling)</p> -<a id="running--submitted"></a><h2>Running / submitted <a class="mdsrc" href="https://github.com/PyAutoLabs/PyAutoCortex/tree/main/phases/">markdown version</a></h2><p class="muted">On the queue or on the machine. Wall is what the run lines record at the last refresh, against the phase's own budget.</p> -<div class="task"><button class="copy" data-cmd="Report where the runs of the PyAutoCortex phase phases/euclid_dr1_prelim/dr1_prelim_10_lens_science_run.md (342301_[0-9], 342314_3) stand." aria-label="Copy the Claude command">📋</button><p><a href="https://github.com/PyAutoLabs/PyAutoCortex/blob/main/phases/euclid_dr1_prelim/dr1_prelim_10_lens_science_run.md">Euclid_dr1_prelim — phase 4: DR1 prelim science run — 10 real Euclid lenses in euclid_dr1_prelim on RAL</a><span class="tags"><span class="pill n">submitted</span></span><span class="facets"> — euclid_dr1_prelim phase 4 · budget 48:00 · 25 review-min · runs 342301_[0-9], 342314_3</span><span class="facets"> — wall 0:00 of 48:00 (0%)</span></p></div> -<div class="task"><button class="copy" data-cmd="cd /mnt/c/Users/Jammy/Science/subhalo_validation && hpc/sync jobs" aria-label="Copy the Claude command">📋</button><p><a href="https://github.com/PyAutoLabs/PyAutoCortex/blob/main/phases/subhalo_validation/delaunay_adapt_split_pl_eff_0.md">Subhalo_validation — phase 2: delaunay_adapt_split on pl_eff_0 — the clumpy-source lens</a><span class="tags"><span class="pill n">submitted</span></span><span class="facets"> — subhalo_validation phase 2 · budget 48:00 · 8 review-min · runs 342027_1, 342231_1, 342234_1, 342299_1</span><span class="facets"> — wall 45:10 of 48:00 (94%)</span></p></div> -<div class="task"><button class="copy" data-cmd="cd /mnt/c/Users/Jammy/Science/subhalo_validation && hpc/sync jobs" aria-label="Copy the Claude command">📋</button><p><a href="https://github.com/PyAutoLabs/PyAutoCortex/blob/main/phases/subhalo_validation/delaunay_adapt_split_pl_eff_1_outer.md">Subhalo_validation — phase 3: delaunay_adapt_split on pl_eff_1_outer — the outer-component lens, plus the AdaptSplit fix rerun</a><span class="tags"><span class="pill n">submitted</span></span><span class="facets"> — subhalo_validation phase 3 · budget 48:00 · 8 review-min · runs 342027_2, 342093_0, 342231_2, 342234_2, 342299_2</span><span class="facets"> — wall 47:05 of 48:00 (98%)</span></p></div> -<div class="task"><button class="copy" data-cmd="cd /mnt/c/Users/Jammy/Science/subhalo_validation && hpc/sync jobs" aria-label="Copy the Claude command">📋</button><p><a href="https://github.com/PyAutoLabs/PyAutoCortex/blob/main/phases/subhalo_validation/rectangular_adapt_pl_sersic_0.md">Subhalo_validation — phase 4: rectangular_adapt on pl_sersic_0 — the RectangularBilinear comparison</a><span class="tags"><span class="pill n">submitted</span></span><span class="facets"> — subhalo_validation phase 4 · budget 48:00 · 15 review-min · runs 342094_0, 342095_0, 342237_0, 342240_0, 342311_0</span><span class="facets"> — wall 18:01 of 48:00 (38%)</span></p></div> -<div class="task"><button class="copy" data-cmd="cd /mnt/c/Users/Jammy/Science/subhalo_validation && hpc/sync jobs" aria-label="Copy the Claude command">📋</button><p><a href="https://github.com/PyAutoLabs/PyAutoCortex/blob/main/phases/subhalo_validation/rectangular_adapt_pl_eff_0.md">Subhalo_validation — phase 5: rectangular_adapt on pl_eff_0 — the clumpy-source lens on the RectangularBilinear comparison</a><span class="tags"><span class="pill n">running</span></span><span class="facets"> — subhalo_validation phase 5 · budget 48:00 · 8 review-min · runs 342237_1, 342240_1, 342311_1</span><span class="facets"> — wall 1:52 of 48:00 (4%)</span></p></div> -<div class="task"><button class="copy" data-cmd="cd /mnt/c/Users/Jammy/Science/subhalo_validation && hpc/sync jobs" aria-label="Copy the Claude command">📋</button><p><a href="https://github.com/PyAutoLabs/PyAutoCortex/blob/main/phases/subhalo_validation/rectangular_adapt_pl_eff_1_outer.md">Subhalo_validation — phase 6: rectangular_adapt on pl_eff_1_outer — the outer-component lens on the RectangularBilinear comparison</a><span class="tags"><span class="pill n">running</span></span><span class="facets"> — subhalo_validation phase 6 · budget 48:00 · 8 review-min · runs 342237_2, 342240_2, 342311_2</span><span class="facets"> — wall 1:52 of 48:00 (4%)</span></p></div> -<a id="ready"></a><h2>Ready <a class="mdsrc" href="https://github.com/PyAutoLabs/PyAutoCortex/tree/main/phases/">markdown version</a></h2><p class="muted">Gate cleared, witness registered — everything that could be submitted today.</p> -<p class="muted">(nothing ready to launch)</p> -<a id="gated"></a><h2>Gated <a class="mdsrc" href="https://github.com/PyAutoLabs/PyAutoCortex/tree/main/phases/">markdown version</a></h2><p class="muted">Waiting on development work. Open the references; when they have all closed, <code>cortex.py move <phase> ready</code>.</p> -<div class="task"><button class="copy" data-cmd="python3 scripts/cortex.py gates # then, once they have closed: move phases/inference_programme/nautilus_mass_pix_baseline.md ready -# gates: autolens_profiling#218" aria-label="Copy the Claude command">📋</button><p><a href="https://github.com/PyAutoLabs/PyAutoCortex/blob/main/phases/inference_programme/nautilus_mass_pix_baseline.md">Inference_programme — phase 21: the Nautilus bar on mass_pix — f1e8 (refs convention) and f1e5 (like-for-like)</a><span class="tags"><span class="pill y">autolens_profiling#218</span></span><span class="facets"> — inference_programme phase 21 · budget 4:00 · 15 review-min</span></p></div> <a id="recent-rulings"></a><h2>Recent rulings <a class="mdsrc" href="https://github.com/PyAutoLabs/PyAutoCortex/tree/main/rulings/">markdown version</a></h2><p class="muted">The ledger of record, newest first. A verdict recorded only outside the Cortex does not exist.</p> <table class="map"> -<tr><th>Ruling</th><th>Verb</th><th>Phase</th><th>Batch</th></tr> -<tr><td><a href="https://github.com/PyAutoLabs/PyAutoCortex/blob/main/rulings/2026/09/R-20260907-01.md">R-20260907-01</a></td><td>accept</td><td>phases/subhalo_validation/delaunay_adapt_split_pl_sersic_0.md</td><td>-</td></tr> -<tr><td><a href="https://github.com/PyAutoLabs/PyAutoCortex/blob/main/rulings/2026/09/R-20260904-05.md">R-20260904-05</a></td><td>drop</td><td>phases/inference_programme/multiband_compile_census_completion.md</td><td>-</td></tr> -<tr><td><a href="https://github.com/PyAutoLabs/PyAutoCortex/blob/main/rulings/2026/09/R-20260904-04.md">R-20260904-04</a></td><td>drop</td><td>phases/inference_programme/mass_pix_gradient_cost_probe.md</td><td>-</td></tr> -<tr><td><a href="https://github.com/PyAutoLabs/PyAutoCortex/blob/main/rulings/2026/09/R-20260904-03.md">R-20260904-03</a></td><td>drop</td><td>phases/inference_programme/cluster_gradient_search_benchmark.md</td><td>-</td></tr> -<tr><td><a href="https://github.com/PyAutoLabs/PyAutoCortex/blob/main/rulings/2026/09/R-20260904-02.md">R-20260904-02</a></td><td>drop</td><td>phases/inference_programme/phase5_mesh_gradient_positions_on.md</td><td>-</td></tr> -<tr><td><a href="https://github.com/PyAutoLabs/PyAutoCortex/blob/main/rulings/2026/09/R-20260904-01.md">R-20260904-01</a></td><td>accept</td><td>phases/inference_programme/refs_v1_positions_on_completion.md</td><td>-</td></tr> -<tr><td><a href="https://github.com/PyAutoLabs/PyAutoCortex/blob/main/rulings/2026/09/R-20260902-10.md">R-20260902-10</a></td><td>accept</td><td>phases/inference_programme/phase4_positions_gate_b2_reuse.md</td><td>2026-09-02-pm</td></tr> -<tr><td><a href="https://github.com/PyAutoLabs/PyAutoCortex/blob/main/rulings/2026/09/R-20260902-09.md">R-20260902-09</a></td><td>accept</td><td>phases/inference_programme/phase3_prodigy_reliability_gate_b1_reuse.md</td><td>2026-09-02-pm</td></tr> -<tr><td><a href="https://github.com/PyAutoLabs/PyAutoCortex/blob/main/rulings/2026/09/R-20260902-08.md">R-20260902-08</a></td><td>accept</td><td>phases/inference_programme/phase2_nss_mainline_gate_a_reuse.md</td><td>2026-09-02-pm</td></tr> -<tr><td><a href="https://github.com/PyAutoLabs/PyAutoCortex/blob/main/rulings/2026/09/R-20260902-07.md">R-20260902-07</a></td><td>rerun</td><td>phases/subhalo_validation/delaunay_adapt_split_pl_sersic_0.md</td><td>-</td></tr> -<tr><td><a href="https://github.com/PyAutoLabs/PyAutoCortex/blob/main/rulings/2026/09/R-20260902-06.md">R-20260902-06</a></td><td>rerun</td><td>phases/subhalo_validation/rectangular_adapt_pl_sersic_0.md</td><td>-</td></tr> -<tr><td><a href="https://github.com/PyAutoLabs/PyAutoCortex/blob/main/rulings/2026/09/R-20260902-05.md">R-20260902-05</a></td><td>rerun</td><td>phases/subhalo_validation/delaunay_adapt_split_pl_eff_0.md</td><td>-</td></tr> +<tr><th>Ruling</th><th>Verb</th><th>Task</th><th>Batch</th></tr> +<tr><td><a href="https://github.com/PyAutoLabs/PyAutoCortex/blob/main/rulings/2026/09/R-20260907-01.md">R-20260907-01</a></td><td>accept</td><td>tasks/subhalo_validation/delaunay_adapt_split_pl_sersic_0.md</td><td>-</td></tr> +<tr><td><a href="https://github.com/PyAutoLabs/PyAutoCortex/blob/main/rulings/2026/09/R-20260904-05.md">R-20260904-05</a></td><td>drop</td><td>tasks/inference_programme/multiband_compile_census_completion.md</td><td>-</td></tr> +<tr><td><a href="https://github.com/PyAutoLabs/PyAutoCortex/blob/main/rulings/2026/09/R-20260904-04.md">R-20260904-04</a></td><td>drop</td><td>tasks/inference_programme/mass_pix_gradient_cost_probe.md</td><td>-</td></tr> +<tr><td><a href="https://github.com/PyAutoLabs/PyAutoCortex/blob/main/rulings/2026/09/R-20260904-03.md">R-20260904-03</a></td><td>drop</td><td>tasks/inference_programme/cluster_gradient_search_benchmark.md</td><td>-</td></tr> +<tr><td><a href="https://github.com/PyAutoLabs/PyAutoCortex/blob/main/rulings/2026/09/R-20260904-02.md">R-20260904-02</a></td><td>drop</td><td>tasks/inference_programme/phase5_mesh_gradient_positions_on.md</td><td>-</td></tr> +<tr><td><a href="https://github.com/PyAutoLabs/PyAutoCortex/blob/main/rulings/2026/09/R-20260904-01.md">R-20260904-01</a></td><td>accept</td><td>tasks/inference_programme/refs_v1_positions_on_completion.md</td><td>-</td></tr> +<tr><td><a href="https://github.com/PyAutoLabs/PyAutoCortex/blob/main/rulings/2026/09/R-20260902-10.md">R-20260902-10</a></td><td>accept</td><td>tasks/inference_programme/phase4_positions_gate_b2_reuse.md</td><td>2026-09-02-pm</td></tr> +<tr><td><a href="https://github.com/PyAutoLabs/PyAutoCortex/blob/main/rulings/2026/09/R-20260902-09.md">R-20260902-09</a></td><td>accept</td><td>tasks/inference_programme/phase3_prodigy_reliability_gate_b1_reuse.md</td><td>2026-09-02-pm</td></tr> +<tr><td><a href="https://github.com/PyAutoLabs/PyAutoCortex/blob/main/rulings/2026/09/R-20260902-08.md">R-20260902-08</a></td><td>accept</td><td>tasks/inference_programme/phase2_nss_mainline_gate_a_reuse.md</td><td>2026-09-02-pm</td></tr> +<tr><td><a href="https://github.com/PyAutoLabs/PyAutoCortex/blob/main/rulings/2026/09/R-20260902-07.md">R-20260902-07</a></td><td>rerun</td><td>tasks/subhalo_validation/delaunay_adapt_split_pl_sersic_0.md</td><td>-</td></tr> +<tr><td><a href="https://github.com/PyAutoLabs/PyAutoCortex/blob/main/rulings/2026/09/R-20260902-06.md">R-20260902-06</a></td><td>rerun</td><td>tasks/subhalo_validation/rectangular_adapt_pl_sersic_0.md</td><td>-</td></tr> +<tr><td><a href="https://github.com/PyAutoLabs/PyAutoCortex/blob/main/rulings/2026/09/R-20260902-05.md">R-20260902-05</a></td><td>rerun</td><td>tasks/subhalo_validation/delaunay_adapt_split_pl_eff_0.md</td><td>-</td></tr> </table> <ul class="boards"><li class="muted" style="padding:.3rem 0">Boards:</li><li><a data-organ="brain" href="https://pyautolabs.github.io/PyAutoBrain/">Brain</a></li><li><a data-organ="mind" href="https://pyautolabs.github.io/PyAutoMind/">Mind</a></li><li><a data-organ="memory" href="https://pyautolabs.github.io/PyAutoMemory/">Memory</a></li><li><a data-organ="heart" href="https://pyautolabs.github.io/PyAutoHeart/">Heart</a></li><li><a data-organ="hands" href="https://pyautolabs.github.io/PyAutoHands/">Hands</a></li><li><a data-organ="organism" href="https://pyautolabs.github.io/PyAutoScientist/">Scientist</a></li></ul> <script>async function copyCmd(b){ diff --git a/dashboard.md b/dashboard.md index e5e9a79..724e0d3 100644 --- a/dashboard.md +++ b/dashboard.md @@ -2,24 +2,21 @@ <!-- generated by `pyauto-brain cortex dashboard --apply` on 2026-09-07 — regenerate, do not hand-edit --> -This is the markdown version of the [PyAutoCortex Dashboard](https://pyautolabs.github.io/PyAutoCortex/), which puts a phase's next command on your clipboard with a single tap of 📋. +This is the markdown version of the [PyAutoCortex Dashboard](https://pyautolabs.github.io/PyAutoCortex/), which puts a task's next command on your clipboard with a single tap of 📋. -Every science phase the Cortex is holding, on one page: what is waiting on your verdict, what is running, what could be launched next and what is still gated on development work. The verdict is always yours — this page hands you the command, never the ruling. +Every science task the Cortex is holding, on one page: one line each under the project it belongs to, coloured by what it is doing — planned, running, waiting on your verdict, or broken. The verdict is always yours — this page hands you the command, never the ruling. > **Last updated 2026-09-07.** | Where | Count | |-------|------:| | [Awaiting ruling](#awaiting-ruling) | 0 | -| [Running / submitted](#running--submitted) | 6 | -| [Ready](#ready) | 0 | -| [Gated](#gated) | 1 | | [Recent rulings](#recent-rulings) | 28 | <details><summary>📋 check in since last time</summary> ``` -/cortex — check in on every active science project since the last check-in (2026-09-07T15:28Z): pull each project through its sync CLI, score every live run against its pre-registered witness, move what came back, re-render and push the board, then read me the by-project summary with the prompt each phase needs next. +/cortex — check in on every active science project since the last check-in (2026-09-07T15:28Z): pull each project through its sync CLI, score every live run against its pre-registered witness, move what came back, re-render and push the board, then read me the by-project summary with the prompt each task needs next. ``` </details> @@ -30,15 +27,15 @@ Every science phase the Cortex is holding, on one page: what is waiting on your One row per active project — the next thing, what it is holding, and when it last ruled. -| Project | Next phase | Awaiting | Running | Ready | Last ruling | -|---|---|---:|---:|---:|---| -| euclid_dr1_prelim | 4 submitted | 0 | 1 | 0 | - | -| inference_programme | 21 gated | 0 | 0 | 0 | 2026-09-04 | -| subhalo_validation | 2 submitted | 0 | 5 | 0 | 2026-09-07 | +| Project | Next task | Awaiting | Running | Last ruling | +|---|---|---:|---:|---| +| euclid_dr1_prelim | Ten real DR1 lenses fitted from the pipeline repo alone | 0 | 1 | - | +| inference_programme | What Nautilus achieves on mass_pix at f1e8 and f1e5 | 0 | 0 | 2026-09-04 | +| subhalo_validation | Does rectangular_adapt false-detect a subhalo on the clumpy source | 0 | 5 | 2026-09-07 | #### Nothing open -| Project | Status | Phases | Retire | +| Project | Status | Tasks | Retire | |---|---|---|---| | concr | dormant | none | retire ↓ | | cowls_diana | dormant | none | retire ↓ | @@ -52,7 +49,7 @@ One row per active project — the next thing, what it is holding, and when it l <details><summary>📋 retire concr</summary> ``` -/cortex — retire the science project concr: confirm it is holding nothing live, then in PyAutoCortex run `python3 scripts/cortex.py retire concr --why "<one line on why>"`, `python3 scripts/cortex.py check`, `pyauto-brain cortex dashboard --apply`, and push the ledger. The row, its phases and its rulings all stay — only the status and the note change. +/cortex — retire the science project concr: confirm it is holding nothing live, then in PyAutoCortex run `python3 scripts/cortex.py retire concr --why "<one line on why>"`, `python3 scripts/cortex.py check`, `pyauto-brain cortex dashboard --apply`, and push the ledger. The row, its tasks and its rulings all stay — only the status and the note change. ``` </details> @@ -60,7 +57,7 @@ One row per active project — the next thing, what it is holding, and when it l <details><summary>📋 retire cowls_diana</summary> ``` -/cortex — retire the science project cowls_diana: confirm it is holding nothing live, then in PyAutoCortex run `python3 scripts/cortex.py retire cowls_diana --why "<one line on why>"`, `python3 scripts/cortex.py check`, `pyauto-brain cortex dashboard --apply`, and push the ledger. The row, its phases and its rulings all stay — only the status and the note change. +/cortex — retire the science project cowls_diana: confirm it is holding nothing live, then in PyAutoCortex run `python3 scripts/cortex.py retire cowls_diana --why "<one line on why>"`, `python3 scripts/cortex.py check`, `pyauto-brain cortex dashboard --apply`, and push the ledger. The row, its tasks and its rulings all stay — only the status and the note change. ``` </details> @@ -68,7 +65,7 @@ One row per active project — the next thing, what it is holding, and when it l <details><summary>📋 retire euclid</summary> ``` -/cortex — retire the science project euclid: confirm it is holding nothing live, then in PyAutoCortex run `python3 scripts/cortex.py retire euclid --why "<one line on why>"`, `python3 scripts/cortex.py check`, `pyauto-brain cortex dashboard --apply`, and push the ledger. The row, its phases and its rulings all stay — only the status and the note change. +/cortex — retire the science project euclid: confirm it is holding nothing live, then in PyAutoCortex run `python3 scripts/cortex.py retire euclid --why "<one line on why>"`, `python3 scripts/cortex.py check`, `pyauto-brain cortex dashboard --apply`, and push the ledger. The row, its tasks and its rulings all stay — only the status and the note change. ``` </details> @@ -76,7 +73,7 @@ One row per active project — the next thing, what it is holding, and when it l <details><summary>📋 retire ic50_workspace</summary> ``` -/cortex — retire the science project ic50_workspace: confirm it is holding nothing live, then in PyAutoCortex run `python3 scripts/cortex.py retire ic50_workspace --why "<one line on why>"`, `python3 scripts/cortex.py check`, `pyauto-brain cortex dashboard --apply`, and push the ledger. The row, its phases and its rulings all stay — only the status and the note change. +/cortex — retire the science project ic50_workspace: confirm it is holding nothing live, then in PyAutoCortex run `python3 scripts/cortex.py retire ic50_workspace --why "<one line on why>"`, `python3 scripts/cortex.py check`, `pyauto-brain cortex dashboard --apply`, and push the ledger. The row, its tasks and its rulings all stay — only the status and the note change. ``` </details> @@ -84,7 +81,7 @@ One row per active project — the next thing, what it is holding, and when it l <details><summary>📋 retire pj011646</summary> ``` -/cortex — retire the science project pj011646: confirm it is holding nothing live, then in PyAutoCortex run `python3 scripts/cortex.py retire pj011646 --why "<one line on why>"`, `python3 scripts/cortex.py check`, `pyauto-brain cortex dashboard --apply`, and push the ledger. The row, its phases and its rulings all stay — only the status and the note change. +/cortex — retire the science project pj011646: confirm it is holding nothing live, then in PyAutoCortex run `python3 scripts/cortex.py retire pj011646 --why "<one line on why>"`, `python3 scripts/cortex.py check`, `pyauto-brain cortex dashboard --apply`, and push the ledger. The row, its tasks and its rulings all stay — only the status and the note change. ``` </details> @@ -92,7 +89,7 @@ One row per active project — the next thing, what it is holding, and when it l <details><summary>📋 retire profiling</summary> ``` -/cortex — retire the science project profiling: confirm it is holding nothing live, then in PyAutoCortex run `python3 scripts/cortex.py retire profiling --why "<one line on why>"`, `python3 scripts/cortex.py check`, `pyauto-brain cortex dashboard --apply`, and push the ledger. The row, its phases and its rulings all stay — only the status and the note change. +/cortex — retire the science project profiling: confirm it is holding nothing live, then in PyAutoCortex run `python3 scripts/cortex.py retire profiling --why "<one line on why>"`, `python3 scripts/cortex.py check`, `pyauto-brain cortex dashboard --apply`, and push the ledger. The row, its tasks and its rulings all stay — only the status and the note change. ``` </details> @@ -100,7 +97,7 @@ One row per active project — the next thing, what it is holding, and when it l <details><summary>📋 retire slope_hierarchy</summary> ``` -/cortex — retire the science project slope_hierarchy: confirm it is holding nothing live, then in PyAutoCortex run `python3 scripts/cortex.py retire slope_hierarchy --why "<one line on why>"`, `python3 scripts/cortex.py check`, `pyauto-brain cortex dashboard --apply`, and push the ledger. The row, its phases and its rulings all stay — only the status and the note change. +/cortex — retire the science project slope_hierarchy: confirm it is holding nothing live, then in PyAutoCortex run `python3 scripts/cortex.py retire slope_hierarchy --why "<one line on why>"`, `python3 scripts/cortex.py check`, `pyauto-brain cortex dashboard --apply`, and push the ledger. The row, its tasks and its rulings all stay — only the status and the note change. ``` </details> @@ -108,7 +105,7 @@ One row per active project — the next thing, what it is holding, and when it l <details><summary>📋 retire subhalo_simulations</summary> ``` -/cortex — retire the science project subhalo_simulations: confirm it is holding nothing live, then in PyAutoCortex run `python3 scripts/cortex.py retire subhalo_simulations --why "<one line on why>"`, `python3 scripts/cortex.py check`, `pyauto-brain cortex dashboard --apply`, and push the ledger. The row, its phases and its rulings all stay — only the status and the note change. +/cortex — retire the science project subhalo_simulations: confirm it is holding nothing live, then in PyAutoCortex run `python3 scripts/cortex.py retire subhalo_simulations --why "<one line on why>"`, `python3 scripts/cortex.py check`, `pyauto-brain cortex dashboard --apply`, and push the ledger. The row, its tasks and its rulings all stay — only the status and the note change. ``` </details> @@ -123,16 +120,18 @@ One row per active project — the next thing, what it is holding, and when it l **RAL** `/mnt/ral/jnightin/euclid_dr1_prelim` -active · both partition · phases: submitted 1 +active · both partition · tasks: submitted 1 -<details><summary>📋 <a href="phases/euclid_dr1_prelim/dr1_prelim_10_lens_science_run.md">Euclid_dr1_prelim — phase 4: DR1 prelim science run — 10 real Euclid lenses in euclid_dr1_prelim on RAL</a> — euclid_dr1_prelim phase 4 · budget 48:00 · 25 review-min · runs 342301_[0-9], 342314_3 — <b>submitted</b></summary> +<details><summary>📋 <a href="tasks/euclid_dr1_prelim/dr1_prelim_10_lens_science_run.md">Ten real DR1 lenses fitted from the pipeline repo alone</a> — <b>submitted</b> — runs 342301_[0-9], 342314_3 · wall 0:00 of 48:00 (0%)</summary> ``` -Report where the runs of the PyAutoCortex phase phases/euclid_dr1_prelim/dr1_prelim_10_lens_science_run.md (342301_[0-9], 342314_3) stand. +Report where the runs of the PyAutoCortex task tasks/euclid_dr1_prelim/dr1_prelim_10_lens_science_run.md (342301_[0-9], 342314_3) stand. ``` </details> +[tasks/euclid_dr1_prelim/](https://github.com/PyAutoLabs/PyAutoCortex/tree/main/tasks/euclid_dr1_prelim/) + ### inference_programme **Local** `/home/jammy/Code/PyAutoLabs/autolens_profiling` @@ -141,74 +140,60 @@ Report where the runs of the PyAutoCortex phase phases/euclid_dr1_prelim/dr1_pre **RAL** `/mnt/ral/jnightin/autolens_profiling` -active · both partition · phases: accepted 7 · dropped 11 · gated 1 · planned 4 +active · both partition · tasks: accepted 7 · dropped 11 · gated 1 · planned 4 -<details><summary>📋 <a href="phases/inference_programme/nautilus_mass_pix_baseline.md">Inference_programme — phase 21: the Nautilus bar on mass_pix — f1e8 (refs convention) and f1e5 (like-for-like)</a> — inference_programme phase 21 · budget 4:00 · 15 review-min — <b>gated</b></summary> +<details><summary>📋 <a href="tasks/inference_programme/nautilus_mass_pix_baseline.md">What Nautilus achieves on mass_pix at f1e8 and f1e5</a> — <b>gated</b> — autolens_profiling#218</summary> ``` -python3 scripts/cortex.py gates # then, once they have closed: move phases/inference_programme/nautilus_mass_pix_baseline.md ready +python3 scripts/cortex.py gates # then, once they have closed: move tasks/inference_programme/nautilus_mass_pix_baseline.md ready # gates: autolens_profiling#218 ``` </details> -<details><summary>4 more open phase(s) · plans and issues</summary> - -- [Inference_programme — phase 11: cluster extended-source inference — gradient-based fitting building on JAX knowledge](phases/inference_programme/cluster_extended_source_inference.md) — planned -- [Inference_programme — phase 19: sweep the point-source families out of the RAL active output/ tree into output/legacy_point/](phases/inference_programme/legacy_point_output_sweep.md) — planned -- [Inference_programme — phase 22: MultiStartProdigy on mass_pix — does a gradient search beat Nautilus in a SLaM mass[1] shape?](phases/inference_programme/prodigy_mass_pix.md) — planned -- [Inference_programme — phase 23: likelihood-term levers on mass_pix — slogdet vs cholesky, relative jitter on reg.Adapt](phases/inference_programme/likelihood_term_levers.md) — planned +<details><summary>📋 <a href="tasks/inference_programme/cluster_extended_source_inference.md">Cluster extended source inference testing gradient based fitting</a> — <b>planned</b></summary> -[phases/inference_programme/](https://github.com/PyAutoLabs/PyAutoCortex/tree/main/phases/inference_programme/) · [issues](https://github.com/PyAutoLabs/autolens_profiling/issues) +``` +python3 scripts/cortex.py move tasks/inference_programme/cluster_extended_source_inference.md ready # when the `Ready when:` clause in its `## Question` is met +``` </details> -### subhalo_validation - -**Local** `/mnt/c/Users/Jammy/Science/subhalo_validation` - -**RAL** `/mnt/ral/jnightin/subhalo_validation` - -active · ral partition · phases: accepted 1 · running 2 · submitted 3 - -<details><summary>📋 <a href="phases/subhalo_validation/delaunay_adapt_split_pl_eff_0.md">Subhalo_validation — phase 2: delaunay_adapt_split on pl_eff_0 — the clumpy-source lens</a> — subhalo_validation phase 2 · budget 48:00 · 8 review-min · runs 342027_1, 342231_1, 342234_1, 342299_1 — <b>submitted</b> — ⚠️ failed runs: 342234_1</summary> +<details><summary>📋 <a href="tasks/inference_programme/legacy_point_output_sweep.md">Can spent point-source families be swept out of active output</a> — <b>planned</b></summary> ``` -cd /mnt/c/Users/Jammy/Science/subhalo_validation && hpc/sync jobs +python3 scripts/cortex.py move tasks/inference_programme/legacy_point_output_sweep.md ready # when the `Ready when:` clause in its `## Question` is met ``` </details> -<details><summary>4 more open phase(s) · plans and issues</summary> +<details><summary>📋 <a href="tasks/inference_programme/likelihood_term_levers.md">Are likelihood terms behind gradient search failure on mass_pix</a> — <b>planned</b></summary> -- [Subhalo_validation — phase 3: delaunay_adapt_split on pl_eff_1_outer — the outer-component lens, plus the AdaptSplit fix rerun](phases/subhalo_validation/delaunay_adapt_split_pl_eff_1_outer.md) — submitted -- [Subhalo_validation — phase 4: rectangular_adapt on pl_sersic_0 — the RectangularBilinear comparison](phases/subhalo_validation/rectangular_adapt_pl_sersic_0.md) — submitted -- [Subhalo_validation — phase 5: rectangular_adapt on pl_eff_0 — the clumpy-source lens on the RectangularBilinear comparison](phases/subhalo_validation/rectangular_adapt_pl_eff_0.md) — running -- [Subhalo_validation — phase 6: rectangular_adapt on pl_eff_1_outer — the outer-component lens on the RectangularBilinear comparison](phases/subhalo_validation/rectangular_adapt_pl_eff_1_outer.md) — running - -[phases/subhalo_validation/](https://github.com/PyAutoLabs/PyAutoCortex/tree/main/phases/subhalo_validation/) · [issues](https://github.com/PyAutoLabs/subhalo_validation/issues) +``` +python3 scripts/cortex.py move tasks/inference_programme/likelihood_term_levers.md ready # when the `Ready when:` clause in its `## Question` is met +``` </details> -## Awaiting ruling +<details><summary>📋 <a href="tasks/inference_programme/prodigy_mass_pix.md">Does a gradient search beat Nautilus on mass_pix</a> — <b>planned</b></summary> -[markdown version](https://github.com/PyAutoLabs/PyAutoCortex/tree/main/phases/) — Results are in and nothing is running — the human's verdict is the only thing outstanding. Ordered failures first, then the phases a ruling is required for, then the clean ones. +``` +python3 scripts/cortex.py move tasks/inference_programme/prodigy_mass_pix.md ready # when the `Ready when:` clause in its `## Question` is met +``` -- _(nothing awaiting a ruling)_ +</details> -## Running / submitted +[tasks/inference_programme/](https://github.com/PyAutoLabs/PyAutoCortex/tree/main/tasks/inference_programme/) · [issues](https://github.com/PyAutoLabs/autolens_profiling/issues) -[markdown version](https://github.com/PyAutoLabs/PyAutoCortex/tree/main/phases/) — On the queue or on the machine. Wall is what the run lines record at the last refresh, against the phase's own budget. +### subhalo_validation -<details><summary>📋 <a href="phases/euclid_dr1_prelim/dr1_prelim_10_lens_science_run.md">Euclid_dr1_prelim — phase 4: DR1 prelim science run — 10 real Euclid lenses in euclid_dr1_prelim on RAL</a> — euclid_dr1_prelim phase 4 · budget 48:00 · 25 review-min · runs 342301_[0-9], 342314_3 — wall 0:00 of 48:00 (0%)</summary> +**Local** `/mnt/c/Users/Jammy/Science/subhalo_validation` -``` -Report where the runs of the PyAutoCortex phase phases/euclid_dr1_prelim/dr1_prelim_10_lens_science_run.md (342301_[0-9], 342314_3) stand. -``` +**RAL** `/mnt/ral/jnightin/subhalo_validation` -</details> +active · ral partition · tasks: accepted 1 · running 2 · submitted 3 -<details><summary>📋 <a href="phases/subhalo_validation/delaunay_adapt_split_pl_eff_0.md">Subhalo_validation — phase 2: delaunay_adapt_split on pl_eff_0 — the clumpy-source lens</a> — subhalo_validation phase 2 · budget 48:00 · 8 review-min · runs 342027_1, 342231_1, 342234_1, 342299_1 — wall 45:10 of 48:00 (94%)</summary> +<details><summary>📋 <a href="tasks/subhalo_validation/rectangular_adapt_pl_eff_0.md">Does rectangular_adapt false-detect a subhalo on the clumpy source</a> — <b>running</b> — runs 342237_1, 342240_1, 342311_1 · wall 1:52 of 48:00 (4%) · ⚠️ failed runs: 342240_1</summary> ``` cd /mnt/c/Users/Jammy/Science/subhalo_validation && hpc/sync jobs @@ -216,7 +201,7 @@ cd /mnt/c/Users/Jammy/Science/subhalo_validation && hpc/sync jobs </details> -<details><summary>📋 <a href="phases/subhalo_validation/delaunay_adapt_split_pl_eff_1_outer.md">Subhalo_validation — phase 3: delaunay_adapt_split on pl_eff_1_outer — the outer-component lens, plus the AdaptSplit fix rerun</a> — subhalo_validation phase 3 · budget 48:00 · 8 review-min · runs 342027_2, 342093_0, 342231_2, 342234_2, 342299_2 — wall 47:05 of 48:00 (98%)</summary> +<details><summary>📋 <a href="tasks/subhalo_validation/rectangular_adapt_pl_eff_1_outer.md">Does rectangular_adapt false-detect a subhalo on the outer-component lens</a> — <b>running</b> — runs 342237_2, 342240_2, 342311_2 · wall 1:52 of 48:00 (4%) · ⚠️ failed runs: 342240_2</summary> ``` cd /mnt/c/Users/Jammy/Science/subhalo_validation && hpc/sync jobs @@ -224,7 +209,7 @@ cd /mnt/c/Users/Jammy/Science/subhalo_validation && hpc/sync jobs </details> -<details><summary>📋 <a href="phases/subhalo_validation/rectangular_adapt_pl_sersic_0.md">Subhalo_validation — phase 4: rectangular_adapt on pl_sersic_0 — the RectangularBilinear comparison</a> — subhalo_validation phase 4 · budget 48:00 · 15 review-min · runs 342094_0, 342095_0, 342237_0, 342240_0, 342311_0 — wall 18:01 of 48:00 (38%)</summary> +<details><summary>📋 <a href="tasks/subhalo_validation/delaunay_adapt_split_pl_eff_0.md">Does delaunay_adapt_split false-detect a subhalo on the clumpy source</a> — <b>submitted</b> — runs 342027_1, 342231_1, 342234_1, 342299_1 · wall 45:10 of 48:00 (94%) · ⚠️ failed runs: 342234_1</summary> ``` cd /mnt/c/Users/Jammy/Science/subhalo_validation && hpc/sync jobs @@ -232,7 +217,7 @@ cd /mnt/c/Users/Jammy/Science/subhalo_validation && hpc/sync jobs </details> -<details><summary>📋 <a href="phases/subhalo_validation/rectangular_adapt_pl_eff_0.md">Subhalo_validation — phase 5: rectangular_adapt on pl_eff_0 — the clumpy-source lens on the RectangularBilinear comparison</a> — subhalo_validation phase 5 · budget 48:00 · 8 review-min · runs 342237_1, 342240_1, 342311_1 — wall 1:52 of 48:00 (4%)</summary> +<details><summary>📋 <a href="tasks/subhalo_validation/delaunay_adapt_split_pl_eff_1_outer.md">Does delaunay_adapt_split false-detect on the outer-component lens and AdaptSplit fix</a> — <b>submitted</b> — runs 342027_2, 342093_0, 342231_2, 342234_2, 342299_2 · wall 47:05 of 48:00 (98%) · ⚠️ failed runs: 342234_2</summary> ``` cd /mnt/c/Users/Jammy/Science/subhalo_validation && hpc/sync jobs @@ -240,7 +225,7 @@ cd /mnt/c/Users/Jammy/Science/subhalo_validation && hpc/sync jobs </details> -<details><summary>📋 <a href="phases/subhalo_validation/rectangular_adapt_pl_eff_1_outer.md">Subhalo_validation — phase 6: rectangular_adapt on pl_eff_1_outer — the outer-component lens on the RectangularBilinear comparison</a> — subhalo_validation phase 6 · budget 48:00 · 8 review-min · runs 342237_2, 342240_2, 342311_2 — wall 1:52 of 48:00 (4%)</summary> +<details><summary>📋 <a href="tasks/subhalo_validation/rectangular_adapt_pl_sersic_0.md">Does rectangular_adapt behave like delaunay_adapt_split on the clean null</a> — <b>submitted</b> — runs 342094_0, 342095_0, 342237_0, 342240_0, 342311_0 · wall 18:01 of 48:00 (38%) · ⚠️ failed runs: 342240_0</summary> ``` cd /mnt/c/Users/Jammy/Science/subhalo_validation && hpc/sync jobs @@ -248,43 +233,32 @@ cd /mnt/c/Users/Jammy/Science/subhalo_validation && hpc/sync jobs </details> -## Ready - -[markdown version](https://github.com/PyAutoLabs/PyAutoCortex/tree/main/phases/) — Gate cleared, witness registered — everything that could be submitted today. +[tasks/subhalo_validation/](https://github.com/PyAutoLabs/PyAutoCortex/tree/main/tasks/subhalo_validation/) · [issues](https://github.com/PyAutoLabs/subhalo_validation/issues) -- _(nothing ready to launch)_ - -## Gated - -[markdown version](https://github.com/PyAutoLabs/PyAutoCortex/tree/main/phases/) — Waiting on development work. Open the references; when they have all closed, `cortex.py move <phase> ready`. +## Awaiting ruling -<details><summary>📋 <a href="phases/inference_programme/nautilus_mass_pix_baseline.md">Inference_programme — phase 21: the Nautilus bar on mass_pix — f1e8 (refs convention) and f1e5 (like-for-like)</a> — inference_programme phase 21 · budget 4:00 · 15 review-min — autolens_profiling#218</summary> +[markdown version](https://github.com/PyAutoLabs/PyAutoCortex/tree/main/tasks/) — Results are in and nothing is running — the human's verdict is the only thing outstanding. Ordered failures first, then the tasks a ruling is required for, then the clean ones. -``` -python3 scripts/cortex.py gates # then, once they have closed: move phases/inference_programme/nautilus_mass_pix_baseline.md ready -# gates: autolens_profiling#218 -``` - -</details> +- _(nothing awaiting a ruling)_ ## Recent rulings [markdown version](https://github.com/PyAutoLabs/PyAutoCortex/tree/main/rulings/) — The ledger of record, newest first. A verdict recorded only outside the Cortex does not exist. -| Ruling | Verb | Phase | Batch | +| Ruling | Verb | Task | Batch | |---|---|---|---| -| [R-20260907-01](rulings/2026/09/R-20260907-01.md) | accept | phases/subhalo_validation/delaunay_adapt_split_pl_sersic_0.md | - | -| [R-20260904-05](rulings/2026/09/R-20260904-05.md) | drop | phases/inference_programme/multiband_compile_census_completion.md | - | -| [R-20260904-04](rulings/2026/09/R-20260904-04.md) | drop | phases/inference_programme/mass_pix_gradient_cost_probe.md | - | -| [R-20260904-03](rulings/2026/09/R-20260904-03.md) | drop | phases/inference_programme/cluster_gradient_search_benchmark.md | - | -| [R-20260904-02](rulings/2026/09/R-20260904-02.md) | drop | phases/inference_programme/phase5_mesh_gradient_positions_on.md | - | -| [R-20260904-01](rulings/2026/09/R-20260904-01.md) | accept | phases/inference_programme/refs_v1_positions_on_completion.md | - | -| [R-20260902-10](rulings/2026/09/R-20260902-10.md) | accept | phases/inference_programme/phase4_positions_gate_b2_reuse.md | 2026-09-02-pm | -| [R-20260902-09](rulings/2026/09/R-20260902-09.md) | accept | phases/inference_programme/phase3_prodigy_reliability_gate_b1_reuse.md | 2026-09-02-pm | -| [R-20260902-08](rulings/2026/09/R-20260902-08.md) | accept | phases/inference_programme/phase2_nss_mainline_gate_a_reuse.md | 2026-09-02-pm | -| [R-20260902-07](rulings/2026/09/R-20260902-07.md) | rerun | phases/subhalo_validation/delaunay_adapt_split_pl_sersic_0.md | - | -| [R-20260902-06](rulings/2026/09/R-20260902-06.md) | rerun | phases/subhalo_validation/rectangular_adapt_pl_sersic_0.md | - | -| [R-20260902-05](rulings/2026/09/R-20260902-05.md) | rerun | phases/subhalo_validation/delaunay_adapt_split_pl_eff_0.md | - | +| [R-20260907-01](rulings/2026/09/R-20260907-01.md) | accept | tasks/subhalo_validation/delaunay_adapt_split_pl_sersic_0.md | - | +| [R-20260904-05](rulings/2026/09/R-20260904-05.md) | drop | tasks/inference_programme/multiband_compile_census_completion.md | - | +| [R-20260904-04](rulings/2026/09/R-20260904-04.md) | drop | tasks/inference_programme/mass_pix_gradient_cost_probe.md | - | +| [R-20260904-03](rulings/2026/09/R-20260904-03.md) | drop | tasks/inference_programme/cluster_gradient_search_benchmark.md | - | +| [R-20260904-02](rulings/2026/09/R-20260904-02.md) | drop | tasks/inference_programme/phase5_mesh_gradient_positions_on.md | - | +| [R-20260904-01](rulings/2026/09/R-20260904-01.md) | accept | tasks/inference_programme/refs_v1_positions_on_completion.md | - | +| [R-20260902-10](rulings/2026/09/R-20260902-10.md) | accept | tasks/inference_programme/phase4_positions_gate_b2_reuse.md | 2026-09-02-pm | +| [R-20260902-09](rulings/2026/09/R-20260902-09.md) | accept | tasks/inference_programme/phase3_prodigy_reliability_gate_b1_reuse.md | 2026-09-02-pm | +| [R-20260902-08](rulings/2026/09/R-20260902-08.md) | accept | tasks/inference_programme/phase2_nss_mainline_gate_a_reuse.md | 2026-09-02-pm | +| [R-20260902-07](rulings/2026/09/R-20260902-07.md) | rerun | tasks/subhalo_validation/delaunay_adapt_split_pl_sersic_0.md | - | +| [R-20260902-06](rulings/2026/09/R-20260902-06.md) | rerun | tasks/subhalo_validation/rectangular_adapt_pl_sersic_0.md | - | +| [R-20260902-05](rulings/2026/09/R-20260902-05.md) | rerun | tasks/subhalo_validation/delaunay_adapt_split_pl_eff_0.md | - | --- From b83d0db2e2f2ad80807e7e6979110680699201f8 Mon Sep 17 00:00:00 2001 From: Jammy2211 <JNightingale2211@gmail.com> Date: Mon, 7 Sep 2026 14:45:44 -0400 Subject: [PATCH 3/3] cortex: re-render the board with white path chips Refs #20. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EpM4gQikgEtCqpBJ3hzgct --- dashboard.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dashboard.html b/dashboard.html index 4400104..77edaee 100644 --- a/dashboard.html +++ b/dashboard.html @@ -223,7 +223,7 @@ a.go:hover{opacity:.9;text-decoration:none} .mdsrc{font-size:.85em} .boards a[data-organ="brain"]{color:#2159c9}@media(prefers-color-scheme:dark){.boards a[data-organ="brain"]{color:#6f9dff}}.boards a[data-organ="mind"]{color:#0a7d72}@media(prefers-color-scheme:dark){.boards a[data-organ="mind"]{color:#2ee6cf}}.boards a[data-organ="cortex"]{color:#a5177d}@media(prefers-color-scheme:dark){.boards a[data-organ="cortex"]{color:#ff7ad9}}.boards a[data-organ="memory"]{color:#6b34d6}@media(prefers-color-scheme:dark){.boards a[data-organ="memory"]{color:#b37eff}}.boards a[data-organ="heart"]{color:#b50f1a}@media(prefers-color-scheme:dark){.boards a[data-organ="heart"]{color:#ff6b73}}.boards a[data-organ="hands"]{color:#9a5400}@media(prefers-color-scheme:dark){.boards a[data-organ="hands"]{color:#ffa733}}.boards a[data-organ="organism"]{color:#0b6fa4}@media(prefers-color-scheme:dark){.boards a[data-organ="organism"]{color:#5ec7f5}} -.fresh{padding:.1rem .9rem;margin:1.2rem 0;background:var(--tint)}.fresh p{margin:.5rem 0}table.map{width:100%;border-collapse:collapse;font-size:.95em}table.map th{text-align:left;color:var(--muted);font-weight:600}table.map td,table.map th{border-bottom:1px solid var(--line);padding:.4rem .5rem .4rem 0;vertical-align:top}.checkin{display:flex;gap:.9rem;align-items:center;padding:.9rem 1.2rem;border:2px solid var(--line);border-radius:.6rem;margin:.2rem 0 1.4rem}.checkin .mark{font-size:2rem;line-height:1}.checkin .label{display:block;font-size:.75rem;letter-spacing:.06em;text-transform:uppercase;color:var(--muted)}.checkin time{font-size:1.6rem;font-weight:700;font-variant-numeric:tabular-nums;display:block}.checkin .note{font-size:.85rem;color:var(--muted)}.checkin.fresh-ok{border-color:var(--ok);background:color-mix(in srgb,var(--ok) 12%,transparent)}.checkin.fresh-ok .mark{color:var(--ok)}.checkin.fresh-bad{border-color:var(--bad);background:color-mix(in srgb,var(--bad) 12%,transparent)}.checkin.fresh-bad .mark{color:var(--bad)}.project h3{font-size:1.35rem;font-weight:700;padding-bottom:.2rem;border-bottom:2px solid var(--accent);letter-spacing:.01em;margin-top:1.6rem}.paths{margin:.2rem 0}.paths b{display:inline-block;min-width:3.6rem}.pathchip{display:inline-block;background:var(--accent);color:var(--accent-ink);font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:.88em;padding:.12em .5em;border-radius:.3rem;overflow-wrap:anywhere}.stale{color:var(--bad);font-weight:600}</style> +.fresh{padding:.1rem .9rem;margin:1.2rem 0;background:var(--tint)}.fresh p{margin:.5rem 0}table.map{width:100%;border-collapse:collapse;font-size:.95em}table.map th{text-align:left;color:var(--muted);font-weight:600}table.map td,table.map th{border-bottom:1px solid var(--line);padding:.4rem .5rem .4rem 0;vertical-align:top}.checkin{display:flex;gap:.9rem;align-items:center;padding:.9rem 1.2rem;border:2px solid var(--line);border-radius:.6rem;margin:.2rem 0 1.4rem}.checkin .mark{font-size:2rem;line-height:1}.checkin .label{display:block;font-size:.75rem;letter-spacing:.06em;text-transform:uppercase;color:var(--muted)}.checkin time{font-size:1.6rem;font-weight:700;font-variant-numeric:tabular-nums;display:block}.checkin .note{font-size:.85rem;color:var(--muted)}.checkin.fresh-ok{border-color:var(--ok);background:color-mix(in srgb,var(--ok) 12%,transparent)}.checkin.fresh-ok .mark{color:var(--ok)}.checkin.fresh-bad{border-color:var(--bad);background:color-mix(in srgb,var(--bad) 12%,transparent)}.checkin.fresh-bad .mark{color:var(--bad)}.project h3{font-size:1.35rem;font-weight:700;padding-bottom:.2rem;border-bottom:2px solid var(--accent);letter-spacing:.01em;margin-top:1.6rem}.paths{margin:.2rem 0}.paths b{display:inline-block;min-width:3.6rem}.pathchip{display:inline-block;background:var(--accent);color:#fff;font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:.88em;padding:.12em .5em;border-radius:.3rem;overflow-wrap:anywhere}@media(prefers-color-scheme:dark){.pathchip{background:color-mix(in srgb,var(--accent) 55%,#000)}}.stale{color:var(--bad);font-weight:600}</style> </head> <body> <header class="hero"><span class="orb"><svg class="mark" viewBox="0 0 48 48" fill="none" stroke="currentColor" stroke-width="1.3" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><circle cx="24" cy="24" r="20.4"/><path d="M11.8,18.6 C14.4,14.8 17.8,14.4 20.1,17.1 C22.4,19.8 25.8,20.2 28.5,17.9 C31.2,15.6 34.4,16.2 36.1,19.1"/><path d="M11.6,24.6 C14.2,20.8 17.6,20.4 19.9,23.1 C22.2,25.8 25.6,26.2 28.3,23.9 C31.0,21.6 34.2,22.2 35.9,25.1"/><path d="M12.8,30.6 C15.3,27.2 18.4,26.8 20.5,29.4 C22.6,32.0 25.7,32.4 28.2,30.2"/><circle cx="32.6" cy="33.0" r="3.8"/><g fill="currentColor" stroke="none"><circle cx="32.6" cy="33.0" r="1.5"/><circle cx="11.7" cy="18.6" r="1.2"/><circle cx="11.5" cy="24.6" r="1.2"/></g></svg></span><h1>PyAuto<b>Cortex</b><span class="kind">Dashboard</span></h1><div class="rule"></div><p class="tag">Question. Run. Rule.</p></header><p class="lede">Every science task the Cortex is holding, one line each under its project. Tap a task's 📋 and its next command is on your clipboard. The verdict is always yours — this page hands you the command, never the ruling.</p>