From 7654757b6411296f01b6986f7ed28eb5bea7eb0f Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 3 Nov 2026 18:10:49 +0000 Subject: [PATCH 1/7] =?UTF-8?q?docs:=20bring=20RTD=20organism=20pages=20cu?= =?UTF-8?q?rrent=20=E2=80=94=20Nerves,=20DNA,=20Broca=20pages;=20drop=20ha?= =?UTF-8?q?rd-coded=20organ=20counts?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit build.md -> hands.md rename left as a human call (changes a published URL). Sphinx warning count unchanged (0 -> 0). Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_016nhQ8REafSSE74S71P4set --- docs/board-orchestration.md | 2 +- docs/concepts/organism.md | 22 +++++++++++------ docs/index.md | 9 ++++--- docs/organs/broca.md | 39 ++++++++++++++++++++++++++++++ docs/organs/build.md | 10 +++++--- docs/organs/dna.md | 45 +++++++++++++++++++++++++++++++++++ docs/organs/nerves.md | 47 +++++++++++++++++++++++++++++++++++++ 7 files changed, 160 insertions(+), 14 deletions(-) create mode 100644 docs/organs/broca.md create mode 100644 docs/organs/dna.md create mode 100644 docs/organs/nerves.md diff --git a/docs/board-orchestration.md b/docs/board-orchestration.md index 8059cbe7..f77ed14a 100644 --- a/docs/board-orchestration.md +++ b/docs/board-orchestration.md @@ -20,7 +20,7 @@ Supply the lowercase `organ` key for the approved action heading. The shared `prompt_heading(organ, heading_id=None)` helper also serves existing owner-specific controls without changing their prompts. It selects fixed text and emphasizes only the organ name with `strong`; it never accepts arbitrary heading HTML. Titles for -legacy callers remain escaped plain text. All thirteen organ headings live in +legacy callers remain escaped plain text. Every organ heading lives in `board/_theme.py`; reuse that mapping rather than copying sentences into renderers. Keep each heading on one line with responsive typography; do not hide overflow or truncate words. Omit explanatory subtitles and preserve work links/copy controls. diff --git a/docs/concepts/organism.md b/docs/concepts/organism.md index b6e561f6..7f1516d9 100644 --- a/docs/concepts/organism.md +++ b/docs/concepts/organism.md @@ -5,7 +5,9 @@ working with libraries and projects. Their boundaries are load-bearing. The cano definition lives in the Brain repo as [ORGANISM.md](https://github.com/PyAutoLabs/PyAutoBrain/blob/main/ORGANISM.md) — one page every organ links to instead of restating. This page summarises -it and adds the framework/instance distinction an adopter needs. +it and adds the framework/instance distinction an adopter needs. The list of +organs grows over time; when this table and ORGANISM.md disagree, ORGANISM.md +and the Mind's `repos.yaml` (every repo with `category: organ`) win. | Organ | Repo | Job | |-------|------|-----| @@ -19,7 +21,9 @@ it and adds the framework/instance distinction an adopter needs. | **Hands** | PyAutoHands | *Does* — packaging, tagging, notebook generation, PyPI releases. A pure executor: never re-derives a gate decision. | | **Pulse** | PyAutoPulse | *Feels* — the cross-project profiling registry, the versioned `profiling-summary` read contract, ingest receipts and dashboard. Project repos produce and hold timings; Pulse validates the exchange and links to them. The Brain's profiling conductor handles judgment. | | **Insight** | PyAutoInsight | Coordinates inference campaign intent and domain tasks; owns the inference summary contract, registry, receipts and evidence board. Projects execute and retain samples; Cortex records scientific conclusions; Mind holds implementation lifecycle and claims. | +| **DNA** | PyAutoDNA | *Remembers what it is made of* — software-stack specifications, environment inventory receipts, support rationale and upgrade/adoption history. Brain coordinates upgrades, Heart validates, Hands releases, Nerves enforces runtime compatibility; collection never upgrades an environment. | | **Nerves** | PyAutoNerves | *Connects* — the configuration/serialization layer (`autonerves`): layered config, the workspace↔library version handshake, `test_mode`, FITS/JSON I/O. The base layer every library imports. | +| **Broca** | PyAutoBroca | *Watches the assistants* — assistant evaluation history, upkeep evidence, collection receipts and the upkeep board. Brain interprets; Mind tracks fixes; the public assistants stay independent of it. | | **Gut** | PyAutoGut | *Sheds* — the lifecycle of condemned self-material (stale branches, stashes, dead code/tests): holds each as a durable, recoverable git ref through a transit window, then **voids** it on a sweep. The storage mirror of Memory (retention ↔ release); the hygiene conductor drives it, as vitals reads Heart. | Everything else — the libraries being developed, their example workspaces, @@ -38,7 +42,7 @@ is the container, and a workspace is one familiar kind of project. These roles do not replace the {doc}`repository categories <../satellites>` or change their workflow and release gates. Profiling, inference and visualization repos illustrate the project role; Eyes, Pulse and Insight provide cross-project organ -views over the visualization and profiling projects. The project-to-organ +views over the visualization, profiling and inference projects. The project-to-organ boundary is described in {doc}`../organs/eyes` and {doc}`../organs/pulse`. ## The call chain @@ -61,11 +65,15 @@ The organs split on one line that matters for adoption: pipelines. Domain facts appear only in declared config surfaces (tables and policy files, not logic), and a drift check — the {ref}`tenant firewall ` — keeps it that way. -- **Instance organs — Mind, Cortex, Memory, Eyes, Ears, Pulse, Insight, Gut.** Committed - state, ledgers, knowledge, visualization and profiling registries and shed - material. These are *inherently yours*: an adopter never forks the upstream - Mind, Cortex, Memory, Eyes, Ears, Pulse, Insight or Gut content, they create their own repos with the same documented - shape. +- **Instance organs — every other organ except Nerves.** Mind, Cortex, + Memory, Eyes, Ears, Pulse, Insight, DNA, Broca and Gut hold committed + state: ledgers, knowledge, visualization, profiling and inference + registries, stack records, assistant evidence and shed material. These are + *inherently yours*: an adopter never forks their upstream content, but + creates their own repos with the same documented shape. +- **Nerves sits outside the split.** It is an organ that also ships a library + package (`autonerves`) the scientific libraries import. You adopt it only if + your own libraries want that config layer; see {doc}`../organs/nerves`. One more principle worth knowing before you read anything else: **one canonical page per fact.** Organ boundaries live in ORGANISM.md; diff --git a/docs/index.md b/docs/index.md index ca0596d5..8c4af9fc 100644 --- a/docs/index.md +++ b/docs/index.md @@ -2,9 +2,9 @@ PyAutoScientist is a working, opinionated reference implementation of an **AI-agent development organism** for human-led, natural-language software -development: a set of ten git repositories — Brain, Mind, Cortex, Memory, -Eyes, Ears, Heart, Hands, Pulse, Insight, Nerves, Gut — through which you lead a multi-repo project in -plain English. You +development: a set of git repositories called **organs** (Brain, Mind, +Cortex, Memory and the rest; {doc}`the organism ` lists +them all) through which you lead a multi-repo project in plain English. You describe what you want; AI agents plan, implement, test, gate and release it; you make every judgment call. @@ -63,6 +63,9 @@ organs/heart organs/build organs/pulse organs/insight +organs/dna +organs/nerves +organs/broca organs/gut ``` diff --git a/docs/organs/broca.md b/docs/organs/broca.md new file mode 100644 index 00000000..bc82db10 --- /dev/null +++ b/docs/organs/broca.md @@ -0,0 +1,39 @@ +# Broca — PyAutoBroca + +**What it owns:** the evidence on how the AI assistants are doing. Broca keeps +assistant evaluation history, upkeep evidence, collection receipts and the +assistant upkeep board. + +**Repo:** [PyAutoLabs/PyAutoBroca](https://github.com/PyAutoLabs/PyAutoBroca) + +## The defining function: watching the assistants speak + +Broca's area is the part of the brain tied to producing language. This organ +watches the organism's public assistants and records how well they work. +Benchmarks and maintenance inventory are kept as separate kinds of evidence, +and a missing or incomparable result never counts as a pass. + +## The boundary that matters + +The public assistants stay independent. They keep their own definitions and +reproducible runners, anyone can use them on their own, and they never depend +on Broca. + +Broca records; it does not judge or act. + +- **Brain** interprets the evidence and routes accepted changes through Mind. +- **Mind** tracks the fixes. +- **Cortex** keeps science records and **Heart** keeps readiness, as everywhere + else. + +Broca is a public repository, so everything committed to it must be suitable +for publication. Raw transcripts and private project data are excluded. It +does not launch model or HPC campaigns on its own, and its Actions workflow +publishes only the rendered saved snapshot, without collecting new evidence. + +## For an adopter + +Broca is an **instance organ**: its evidence is about *your* assistants. If you +publish assistants of your own, create a Broca with the same shape. The full +boundary is the +[Broca section of ORGANISM.md](https://github.com/PyAutoLabs/PyAutoBrain/blob/main/ORGANISM.md#broca--assistant-evidence). diff --git a/docs/organs/build.md b/docs/organs/build.md index a951414d..67e86037 100644 --- a/docs/organs/build.md +++ b/docs/organs/build.md @@ -12,9 +12,13 @@ ship. The Hands are a **pure executor**: no readiness checks of their own, no re-deriving of gate decisions. Readiness is gated upstream — the Brain's release path consults the vitals faculty, which reads Heart, and only a -GREEN verdict reaches the dispatch. Historically Build owned some checking; -that all migrated to Heart, and thin shims remain so old entry points still -work. +GREEN verdict reaches the dispatch. Historically the Hands (then the +PyAutoBuild repo) owned some checking; that all migrated to Heart, and thin +shims remain so old entry points still work. + +*Build* is still the name of the call-chain step the Hands perform +(`Brain → Heart → Build`), which is why you will see both words. The organ is +the Hands. ## The pieces diff --git a/docs/organs/dna.md b/docs/organs/dna.md new file mode 100644 index 00000000..79c6f6c8 --- /dev/null +++ b/docs/organs/dna.md @@ -0,0 +1,45 @@ +# DNA — PyAutoDNA + +**What it owns:** the identity of the software stack. DNA keeps the +environment registry and immutable stack specifications, the rationale for +what is supported, inventory receipts showing what each local, RAL and CI +environment actually runs, and the history of upgrades and adoptions. + +**Repo:** [PyAutoLabs/PyAutoDNA](https://github.com/PyAutoLabs/PyAutoDNA) + +## The defining function: knowing what you are made of + +DNA records facts and keeps them apart. Upstream availability, declared +compatibility, tested combinations, recommended stacks and observed +installations are separate facts, and DNA never merges them into one guess. +Source checkouts are identified by commit and dirty state, published wheels by +version and source provenance, and workspace declarations are treated as +compatibility floors. + +Collecting an inventory never upgrades an environment, and a missing +observation never counts as a pass. + +## The driver split + +DNA keeps the records; other organs act on them. + +- **Brain** coordinates upgrade proposals using DNA records and the existing + development and release workflows. +- **Heart** keeps the authoritative validation and readiness verdict. DNA links + to that evidence rather than re-deriving it. +- **Hands** keeps release execution and provenance. +- **Nerves** enforces compatibility at runtime. +- **Mind** tracks the implementation tasks. + +Project campaigns keep their original stack identity. Adopting a new stack +changes the default for new work only, and only through an explicit operation. +Dashboard refreshes collect and render; they never install packages, submit +compute or change a live environment. + +## For an adopter + +Like Mind and Memory, DNA is an **instance organ**: the stack specifications +and inventory receipts describe *your* environments. You create your own DNA +with the same shape rather than forking this repo's records. The full boundary +is the +[software-stack section of ORGANISM.md](https://github.com/PyAutoLabs/PyAutoBrain/blob/main/ORGANISM.md#software-stack-identity-and-adoption). diff --git a/docs/organs/nerves.md b/docs/organs/nerves.md new file mode 100644 index 00000000..5ce6c349 --- /dev/null +++ b/docs/organs/nerves.md @@ -0,0 +1,47 @@ +# Nerves — PyAutoNerves + +**What it owns:** the configuration and serialization layer that connects the +organism's conventions to every scientific library. It ships as the +`autonerves` package: layered config with overrides, the workspace↔library +version handshake, `test_mode`, and FITS/JSON I/O. PyAutoFit, PyAutoArray, +PyAutoGalaxy and PyAutoLens all import it as their base layer. + +**Repo:** [PyAutoLabs/PyAutoNerves](https://github.com/PyAutoLabs/PyAutoNerves) +· live board: + +The repo was renamed from PyAutoConf, and its package from `autoconf` to +`autonerves`. Older code and notes may still use the old names. + +## The defining function: connecting + +Nerves carry signals from one part of the body to another. This organ does the +same for conventions: a setting written once in a workspace's config reaches +every library that reads it, and the version handshake tells a workspace +straight away when it is talking to a library it was not written for. + +## An organ that is also a library + +Most organs own state or effects and leave libraries alone. Nerves is the +exception: it is an organ that also supplies a library package. The +[responsibility roles](https://github.com/PyAutoLabs/PyAutoBrain/blob/main/ORGANISM.md#responsibility-roles) +in ORGANISM.md describe this directly: roles are responsibilities, not levels +in a tree, so one repo can carry both. + +This is also where new config surfaces go. The organism's growth rule says new +configuration or signalling belongs in Nerves, not in a new organ. + +## Its neighbours + +- **DNA** records which software stacks are specified, tested and installed; + Nerves enforces compatibility at runtime through the version handshake. +- **Heart** checks version skew across repos as part of its readiness verdict. + Nerves catches a mismatch at runtime; Heart catches it before a release. + +## For an adopter + +Nerves sits outside the framework/instance split described in +{doc}`../concepts/organism`: it is neither framework you fork nor state you +recreate. You need it only if your own libraries want a shared config and +serialization layer. If they do, the `autonerves` package is the pattern to +follow: one base layer every library imports, with configuration declared in +files rather than scattered through code. From 557f8e07126db5e9a55f8f1cb4fc73e1441fbf79 Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 3 Nov 2026 18:11:33 +0000 Subject: [PATCH 2/7] fix(intake): no Memory: line on organ-targeted prompts _cites_memory() gates on ORGANISM_REPOS (category: organ in repos.yaml); write_prompt skips the Memory query for organ targets. Library targets with digest hits still get the line. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_016nhQ8REafSSE74S71P4set --- agents/conductors/intake/_intake.py | 29 +++++++++++++++++---- tests/test_intake_memory.py | 40 +++++++++++++++++++++++++++++ 2 files changed, 64 insertions(+), 5 deletions(-) diff --git a/agents/conductors/intake/_intake.py b/agents/conductors/intake/_intake.py index 92a80adb..f9f912c5 100755 --- a/agents/conductors/intake/_intake.py +++ b/agents/conductors/intake/_intake.py @@ -781,6 +781,12 @@ def _next_action(proposed, confidence, work_type=None): # prompt with no hits, or filed on a box with no PyAutoMemory, gets no line at # all: an empty `Memory:` would read as "nothing is known", which is a claim # this has no standing to make. +# +# Nor does a prompt that targets an organ (Brain, Mind, Heart, ...: the body +# map's `category: organ` repos, i.e. ORGANISM_REPOS). Its subject is the +# organism's own machinery, so there is no science question to answer, and the +# digest only ever matched on shared vocabulary ("profile", "model") — every +# organ prompt filed got galaxy-wiki citations that had to be hand-removed. MEMORY_CITATIONS = 3 MEMORY_QUERY_WORDS = 200 @@ -811,9 +817,20 @@ def memory_citations(mind: Path, title: str, body_text: str, return [page["page"] for page in d["pages"]][:limit] -def _with_memory(header: str, pages: list) -> str: - """`Memory: a.md; b.md` under the optional keys, or the header unchanged.""" - if not pages: +def _cites_memory(target: str | None) -> bool: + """Whether a prompt with this `Target:` key carries a `Memory:` line. + + False for an organ target: see the block comment above. + """ + return normalise_repo(target or "") not in ORGANISM_REPOS + + +def _with_memory(header: str, pages: list, target: str | None = None) -> str: + """`Memory: a.md; b.md` under the optional keys, or the header unchanged. + + Unchanged when there are no pages, or when `target` is an organ repo. + """ + if not pages or not _cites_memory(target): return header line = "Memory: " + "; ".join(pages) lines = header.split("\n") @@ -841,9 +858,11 @@ def write_prompt(mind: Path, decision: dict, body_text: str, source_note: str): # touches no filesystem, and a citation is only worth computing for a # prompt that is actually going to exist. The decision carries the header # that was written, so `--json` reports the file's real contents. - cites = memory_citations(mind, decision.get("title", ""), body_text) + target = decision.get("target") + cites = (memory_citations(mind, decision.get("title", ""), body_text) + if _cites_memory(target) else []) decision["memory"] = cites - decision["header"] = _with_memory(decision["header"], cites) + decision["header"] = _with_memory(decision["header"], cites, target) # The input's own header block (and its heading) is already rendered in # the decision's header — do not echo it into the body (PyAutoBrain#472). _heading, header_lines, body = _split_header(body_text) diff --git a/tests/test_intake_memory.py b/tests/test_intake_memory.py index b14d5b87..0ef896d6 100644 --- a/tests/test_intake_memory.py +++ b/tests/test_intake_memory.py @@ -130,3 +130,43 @@ def test_seed_stubs_are_never_cited(tmp_path, monkeypatch): encoding="utf-8") line = _memory_line(_file_one(mind)) assert line and "seed/" not in line + + +def _declared(target: str) -> str: + return (f"# Subhalo sensitivity map\n\nType: feature\nTarget: {target}\n" + f"Repos:\n- {target}\n\n{RAW}\n") + + +def test_organ_target_gets_no_memory_line(tmp_path, monkeypatch): + """An organ-targeted prompt is about the organism's own machinery: there is + no science question, and the digest only matches on shared vocabulary.""" + monkeypatch.delenv("PYAUTO_MEMORY", raising=False) + mind = _mind(tmp_path) + _memory(tmp_path) + organs = ("PyAutoBrain", "PyAutoMind", "PyAutoCortex", "PyAutoHeart", + "PyAutoHands", "PyAutoNerves", "PyAutoEyes", "PyAutoPulse", + "PyAutoInsight", "PyAutoMemory", "PyAutoBroca", "PyAutoGut", + "PyAutoDNA", "PyAutoEars") + for organ in organs: + raw = _declared(organ) + decision = _intake.analyse(raw, "test") + assert decision["target_source"] == "declared", organ + rel = _intake.write_prompt(mind, decision, raw, "test") + text = (mind / rel).read_text(encoding="utf-8") + assert "Memory:" not in text, organ + assert decision["memory"] == [], organ + # the gate holds at `_with_memory` itself, not only in write_prompt + assert _intake._with_memory("Priority: normal", ["wiki/x.md"], + decision["target"]) == "Priority: normal" + + +def test_library_target_with_hits_still_gets_memory_line(tmp_path, monkeypatch): + monkeypatch.delenv("PYAUTO_MEMORY", raising=False) + mind = _mind(tmp_path) + _memory(tmp_path) + raw = _declared("PyAutoLens") + decision = _intake.analyse(raw, "test") + assert decision["target_source"] == "declared" + rel = _intake.write_prompt(mind, decision, raw, "test") + line = _memory_line((mind / rel).read_text(encoding="utf-8")) + assert "wiki/lensing/concepts/subhalo.md" in line From 15317afcbd447f2e673e2c5711af26ad5570d547 Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 3 Nov 2026 18:12:23 +0000 Subject: [PATCH 3/7] =?UTF-8?q?feat(board):=20phase=202=20=E2=80=94=20the?= =?UTF-8?q?=20remaining=20GitHub=20legs=20onto=20the=20--github-data=20sea?= =?UTF-8?q?m?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit versions/community/resume were retired from collect() in 41ec5c5, so no script needs its own seam; upkeep (the only live GitHub leg left) gets a named endpoint that is both its gh api path and its --github-data key. Fixture-driven test: zero unread legs with data injected; a null reads as 'open-issue count unavailable', never 0. Gatherer skill and contract updated. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_016nhQ8REafSSE74S71P4set --- board/AGENTS.md | 7 ++++ board/_board.py | 16 +++++++-- skills/board/board.md | 18 +++++----- tests/test_board_github_data.py | 61 +++++++++++++++++++++++++++++++++ 4 files changed, 91 insertions(+), 11 deletions(-) diff --git a/board/AGENTS.md b/board/AGENTS.md index 2899d9f8..b88483b5 100644 --- a/board/AGENTS.md +++ b/board/AGENTS.md @@ -70,6 +70,13 @@ verbatim. This adapter supports retained sources; it does not re-enable retired overnight collection. Legacy explicit diagnostic helpers remain for compatibility, separate from board publication. +The board's one remaining GitHub leg is upkeep: key +`search/issues?q=org:+is:issue+is:open&per_page=1` +(`open_issues_endpoint()`), value GitHub's search response verbatim (the leg +reads `total_count`). Versions, community and resume are retired and not +collected, so `bin/version_drift.sh` and the community scan need no seam of +their own; a retired leg returns only through this same endpoint-keyed map. + `BOARD_GH` and `BOARD_PAGES_BASE` support hermetic fixtures. A failed Pages read may use an owning repo's local published artifact; it must not manufacture freshness. Public artifacts must never leak private local paths or credentials. diff --git a/board/_board.py b/board/_board.py index 22e9feb3..168df630 100755 --- a/board/_board.py +++ b/board/_board.py @@ -112,7 +112,7 @@ def derive_org(homes): # --------------------------------------------------------------------------- # The injection seam (--github-data) # -# Eleven of this board's legs read GitHub through `gh api`, and a Claude Code +# The legs of this board that read GitHub do so through `gh api`, and a Claude Code # remote session — the surface the board is actually read from on a phone — has # no `gh` at all. Its GitHub access is the `mcp__github__*` tool surface, which # is an AGENT capability: this file is a subprocess and cannot reach it, however @@ -475,10 +475,22 @@ def extract_heart_performance(board, board_url=""): } +def open_issues_endpoint(org): + """The upkeep leg's `gh api` endpoint — also its `--github-data` key. + + Upkeep is the board's only remaining GitHub leg (versions, community and + resume were retired with the agents/workflows simplification and are not + collected at all), so this is the one key a gh-less gatherer must supply. + The response is GitHub's search payload verbatim; the leg reads + `total_count`. + """ + return f"search/issues?q=org:{org}+is:issue+is:open&per_page=1" + + def collect_open_issues(org, degraded): """Total open issues across the org — the /issue_cleanup pointer count (the audit itself stays that skill's confirmation-gated job).""" - res = gh_json([f"search/issues?q=org:{org}+is:issue+is:open&per_page=1"]) + res = gh_json([open_issues_endpoint(org)]) if res is None: degraded.append("upkeep: open-issue count unavailable") return None diff --git a/skills/board/board.md b/skills/board/board.md index 797f1f11..4a4e3043 100644 --- a/skills/board/board.md +++ b/skills/board/board.md @@ -13,21 +13,21 @@ nothing; every chip on it routes back through the real doors. **Without `gh` (mobile, web), gather first — you are the only thing that can.** The `mcp__github__*` tools are an agent capability; `_board.py` is a - subprocess and cannot reach them. So fetch the overnight legs yourself, + subprocess and cannot reach them. So fetch the GitHub legs yourself, write them to a scratch file keyed by endpoint, and hand it over: ``` bin/pyauto-brain board --github-data /tmp/board-github-data.json ``` - For each `overnight_jobs` entry in `config/policy.yaml` - (`:`), one `mcp__github__actions_list` - (`list_workflow_runs`, `resource_id` = the workflow file, `per_page` 1), - stored under `repos///actions/workflows//runs?per_page=1`. - Add `.../actions/runs//jobs` (`list_workflow_jobs`) where you want the - blocked-at-a-gate refinement. Store each response **verbatim**: the row - reads `conclusion`, `status`, `created_at` (**not** `updated_at` — that one - renders the age as `?`) and `html_url`. The file's shape, and the three rules a + Today that is one key — upkeep's open-issue count, stored under + `search/issues?q=org:+is:issue+is:open&per_page=1` as GitHub's search + response verbatim (the leg reads `total_count`). If your tools cannot run + that search, store `null`: the leg then says it is unread rather than + reporting zero. Overnight, versions, community and resume are retired and + need nothing. Store each response **verbatim**: field names are part of the + contract (the retired overnight row, for instance, read `created_at`, never + `updated_at`). The file's shape, and the three rules a gatherer must not bend — a miss is *could not ask*, an explicit `null` says the fetch failed, a broken file is fatal rather than degrading — are the contract in [`../../board/AGENTS.md`](../../board/AGENTS.md) → "Reading the diff --git a/tests/test_board_github_data.py b/tests/test_board_github_data.py index d8ea8f28..0baed42d 100644 --- a/tests/test_board_github_data.py +++ b/tests/test_board_github_data.py @@ -144,3 +144,64 @@ def test_an_uninjected_workflow_stays_unreadable_and_degrades(tmp_path): assert rows[0]["unreadable"] is True assert degraded, "a leg that could not be read must degrade the render" + + +# -------------------------------------------------------------------------- +# Phase 2: versions, community, resume, upkeep +# +# Of the four legs phase 1 left dark, three (versions, community, resume) were +# retired with the agents/workflows simplification: `collect()` no longer runs +# `bin/version_drift.sh`, the community scan or the pending-release search, so +# neither those scripts nor the board need a seam for them. Upkeep is the one +# that still reads GitHub, and it already goes through `gh_json` — so the +# phase-1 seam covers it with one more key and no second shape. +# -------------------------------------------------------------------------- + +PHASE2_LEGS = ("versions", "community", "resume", "upkeep") + + +def _remote_collect(monkeypatch): + """`collect()` as a gh-less session runs it, with only the non-GitHub + sources (body map, Pages, local subprocess legs) stubbed out.""" + monkeypatch.setattr(_board, "load_policy", dict) + monkeypatch.setattr(_board, "repo_homes", list) + monkeypatch.setattr(_board, "derive_org", lambda _homes: "Org") + monkeypatch.setattr(_board, "fetch_heart_board", lambda *a: {}) + monkeypatch.setattr(_board, "collect_hygiene", lambda _degraded: None) + monkeypatch.setattr(_board, "collect_devbox", lambda: None) + monkeypatch.setattr(_board, "collect_doors", list) + return _board.collect() + + +def _phase2_unread(data): + return [d for d in data["degraded"] if d.split(":", 1)[0] in PHASE2_LEGS] + + +def test_a_remote_render_reads_all_four_phase2_legs(tmp_path, monkeypatch): + """The headline criterion: with `--github-data`, zero of the four unread.""" + _board.load_github_data(_write(tmp_path, { + # GitHub's search payload, verbatim; the leg reads `total_count`. + _board.open_issues_endpoint("Org"): { + "total_count": 17, "incomplete_results": False, "items": []}, + })) + + data = _remote_collect(monkeypatch) + + assert _phase2_unread(data) == [], data["degraded"] + assert data["open_issues"] == 17 + # The retired legs are compatibility placeholders, never fetched. + assert data["community"] is None + assert data["versions"]["stamps"] == [] + assert data["resume"]["pending_prs"] == [] + + +def test_without_the_upkeep_key_the_leg_names_itself_unread(tmp_path, monkeypatch): + """A gatherer that could not ask leaves upkeep dark — and says so, rather + than reporting zero open issues.""" + _board.load_github_data(_write(tmp_path, { + _board.open_issues_endpoint("Org"): None})) + + data = _remote_collect(monkeypatch) + + assert _phase2_unread(data) == ["upkeep: open-issue count unavailable"] + assert data["open_issues"] is None From 1d9e37e1be192e38b10235c411cea741bff43c50 Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 3 Nov 2026 18:36:47 +0000 Subject: [PATCH 4/7] test(intake): read the library target from repos.yaml (tenant firewall) Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_016nhQ8REafSSE74S71P4set --- tests/test_intake_memory.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_intake_memory.py b/tests/test_intake_memory.py index 0ef896d6..489009e1 100644 --- a/tests/test_intake_memory.py +++ b/tests/test_intake_memory.py @@ -164,7 +164,7 @@ def test_library_target_with_hits_still_gets_memory_line(tmp_path, monkeypatch): monkeypatch.delenv("PYAUTO_MEMORY", raising=False) mind = _mind(tmp_path) _memory(tmp_path) - raw = _declared("PyAutoLens") + raw = _declared(sorted(_intake.LIBRARY_REPOS)[0]) # any library, read from repos.yaml decision = _intake.analyse(raw, "test") assert decision["target_source"] == "declared" rel = _intake.write_prompt(mind, decision, raw, "test") From e0a29ca6ed8886fdfab914ac49dd540e423795db Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 3 Nov 2026 19:12:00 +0000 Subject: [PATCH 5/7] fix(install): project_discovery accepts repo names after --root (#565) install.sh passes --root before the names; parse_args() had already bound the nargs='*' positional, so every named check/write errored. parse_intermixed_args. test_skill_install.py: 10 failed -> 22 passed. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_016nhQ8REafSSE74S71P4set --- bin/project_discovery.py | 2 +- docs/organs/{build.md => hands.md} | 0 2 files changed, 1 insertion(+), 1 deletion(-) rename docs/organs/{build.md => hands.md} (100%) diff --git a/bin/project_discovery.py b/bin/project_discovery.py index 5e149ddc..e567d7fa 100644 --- a/bin/project_discovery.py +++ b/bin/project_discovery.py @@ -326,7 +326,7 @@ def main() -> int: parser.add_argument('mode', choices=('write', 'check', 'starter-write', 'starter-check')) parser.add_argument('repos', nargs='*') parser.add_argument('--root', type=Path, default=Path(os.environ.get('PYAUTO_ROOT', '.'))) - args = parser.parse_args() + args = parser.parse_intermixed_args() # install.sh puts --root before the repo names try: if args.mode.startswith('starter-'): if args.repos: diff --git a/docs/organs/build.md b/docs/organs/hands.md similarity index 100% rename from docs/organs/build.md rename to docs/organs/hands.md From ba38413b66495d3029a4e38c7d672b1d3a46e970 Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 3 Nov 2026 19:12:00 +0000 Subject: [PATCH 6/7] fix(intake): repos under organs/ (e.g. the landing repo) also get no Memory: line (#561) Human decision 2026-11-03. Read from the body map's path, so no repo is named in organ code. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_016nhQ8REafSSE74S71P4set --- agents/conductors/intake/_intake.py | 23 ++++++++++++++++++++--- tests/test_intake_memory.py | 4 ++++ 2 files changed, 24 insertions(+), 3 deletions(-) diff --git a/agents/conductors/intake/_intake.py b/agents/conductors/intake/_intake.py index f9f912c5..0c51b87e 100755 --- a/agents/conductors/intake/_intake.py +++ b/agents/conductors/intake/_intake.py @@ -41,7 +41,7 @@ WORK_TYPES, HUMAN_REVIEW, MANUAL_ONLY_WORK_TYPES, LIBRARY_REPOS, WORKSPACE_REPOS, ORGANISM_REPOS, KNOWN_REPOS, RISK_KEYWORDS, AMBIGUITY_KEYWORDS, normalise_repo, norm_work_type, - declared_header, LANE_VALUES, + declared_header, LANE_VALUES, _body_map_specs, declared_inline, effective_difficulty, strip_declarations, _hits, effective_consequence, effective_unattended, effective_review_minutes, policy as _sizing_policy, BODY_MAP_PATH, @@ -817,12 +817,29 @@ def memory_citations(mind: Path, title: str, body_text: str, return [page["page"] for page in d["pages"]][:limit] +def _organs_dir_repos() -> frozenset: + """Normalised names of body-map repos checked out under `organs/`.""" + try: + specs = _body_map_specs() + except (OSError, KeyError, ValueError): + return frozenset() + return frozenset(normalise_repo(name) for name, spec in specs.items() + if str(spec.get("path", "")).startswith("organs/")) + + +_ORGANS_DIR_REPOS = _organs_dir_repos() + + def _cites_memory(target: str | None) -> bool: """Whether a prompt with this `Target:` key carries a `Memory:` line. - False for an organ target: see the block comment above. + False for an organ target: see the block comment above. "Organ" here also + covers any repo whose body-map `path` sits under `organs/` (e.g. the + umbrella landing repo, `category: project` but organism machinery, not + science). """ - return normalise_repo(target or "") not in ORGANISM_REPOS + key = normalise_repo(target or "") + return key not in ORGANISM_REPOS and key not in _ORGANS_DIR_REPOS def _with_memory(header: str, pages: list, target: str | None = None) -> str: diff --git a/tests/test_intake_memory.py b/tests/test_intake_memory.py index 489009e1..7c25387e 100644 --- a/tests/test_intake_memory.py +++ b/tests/test_intake_memory.py @@ -147,6 +147,10 @@ def test_organ_target_gets_no_memory_line(tmp_path, monkeypatch): "PyAutoHands", "PyAutoNerves", "PyAutoEyes", "PyAutoPulse", "PyAutoInsight", "PyAutoMemory", "PyAutoBroca", "PyAutoGut", "PyAutoDNA", "PyAutoEars") + # plus every non-organ repo the body map checks out under organs/ + extra = tuple(sorted(_intake._ORGANS_DIR_REPOS - _intake.ORGANISM_REPOS)) + assert extra, "expected a non-organ repo under organs/ in repos.yaml" + organs = organs + extra for organ in organs: raw = _declared(organ) decision = _intake.analyse(raw, "test") From 1aa2ed4655f4242c8a5f42eff3ff62717014d97c Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 3 Nov 2026 19:12:00 +0000 Subject: [PATCH 7/7] docs: rename organs/build -> organs/hands with a redirect; Broca in the organ table (#563) Human decisions 2026-11-03. build.html stays as a meta-refresh to hands.html (html_extra_path). ORGANISM.md growth paragraph no longer claims a count of five. nerves.md is a deliberate PyAutoConf mention. Sphinx warnings: 0. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_016nhQ8REafSSE74S71P4set --- ORGANISM.md | 5 +++-- docs/_extra/organs/build.html | 6 ++++++ docs/conf.py | 1 + docs/index.md | 2 +- tests/test_nerves_rename_complete.py | 2 ++ 5 files changed, 13 insertions(+), 3 deletions(-) create mode 100644 docs/_extra/organs/build.html diff --git a/ORGANISM.md b/ORGANISM.md index c672777d..16b33357 100644 --- a/ORGANISM.md +++ b/ORGANISM.md @@ -22,6 +22,7 @@ organs plan, build, test and release it, and you make every judgment call. | **Hands** | PyAutoHands | Builds and releases — packaging, tagging, notebook generation, PyPI via `release.yml`. A pure executor: runs no readiness checks and never re-derives a gate decision. | | **Pulse** | PyAutoPulse | Feels *how fast the organism runs* across the `_profiling` project repos. Owns profiling campaign intent and pending domain tasks, the profiling instance registry, the versioned `profiling-summary` read contract, the ingest receipts (resolved commit per project per render) and the cross-project board. Validates the exchange contract only; project repos own producers, results, pins, drift policy and their own Pages page. Never moves pins, combines unmatched timings, applies the compile threshold to runtime, computes an ecosystem-wide speed score or issues a readiness verdict; the Brain's profiling conductor (`triage`) is the only judge. | | **Insight** | PyAutoInsight | Owns inference campaign intent and pending domain tasks, the cross-project inference instance registry, versioned `inference-summary` read contract, ingest receipts and evidence dashboard. Projects own execution, producers and raw samples; Cortex owns scientific run records, observations and human conclusions; Mind owns bounded implementation lifecycle and repository claims. Never infers scientific acceptance from execution, ranks incompatible runs or submits compute on refresh. | +| **Broca** | PyAutoBroca | Owns assistant evidence — evaluation history, collection receipts and the assistant upkeep board (public, so committed evidence must be publishable: no raw transcripts or private data). Public assistants keep their definitions and runners and never depend on Broca; Brain interprets the evidence and routes accepted changes through Mind. Missing or incomparable results never establish a pass, and Broca never launches model or HPC campaigns. | | **DNA** | PyAutoDNA | Owns software-stack specifications, environment inventory receipts, compatibility decisions, upgrade campaigns and adoption history across local, RAL and CI. Brain coordinates updates; Heart owns validation/readiness; Hands owns release execution/provenance; Nerves owns runtime compatibility. Package repositories remain authoritative for requirements. Collection never upgrades an environment or treats missing observations as passing evidence. | | **Nerves** | PyAutoNerves | The configuration/serialization layer (`autonerves`) — layered config with overrides, the workspace↔library version handshake, `test_mode`, FITS/JSON I/O. Connects the organism's conventions to every library; the base layer the scientific libraries all import. | | **Gut** | PyAutoGut | Owns the lifecycle of *condemned self-material* — stale branches, stashes, dead code/tests. Holds each as a durable, recoverable git ref through a transit window and **voids** it on a sweep. The storage mirror of Memory (retention ↔ release); the hygiene conductor drives it, as vitals reads Heart. | @@ -118,8 +119,8 @@ human types); let faculties multiply behind them. New capability grows as a **faculty** (cheap: one directory, one doc, one script), not as a repo. A new organ costs an `AGENTS.md`, install wiring, a body-map row and boundary prose — it must earn that by -owning state or effects no existing organ can. Five capabilities have earned -organ status that way. Configuration/signalling is the **Nerves** +owning state or effects no existing organ can. Capabilities keep earning +organ status that way; the first five were these. Configuration/signalling is the **Nerves** (PyAutoNerves), the base config/serialization layer every library imports — new config surfaces belong there, not in a new organ. Keeping track of what is true is the **Cortex** (PyAutoCortex), the second organ to earn it: it owns diff --git a/docs/_extra/organs/build.html b/docs/_extra/organs/build.html new file mode 100644 index 00000000..56347b9a --- /dev/null +++ b/docs/_extra/organs/build.html @@ -0,0 +1,6 @@ + + +Moved: Hands + + +

This page moved to Hands — PyAutoHands.

diff --git a/docs/conf.py b/docs/conf.py index 2407aa94..36a1aad3 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -26,6 +26,7 @@ language = "en" html_static_path = ["_static"] +html_extra_path = ["_extra"] # e.g. organs/build.html -> hands.html after the rename html_css_files = ["pyauto.css"] html_theme_options = { diff --git a/docs/index.md b/docs/index.md index 8c4af9fc..1614e995 100644 --- a/docs/index.md +++ b/docs/index.md @@ -60,7 +60,7 @@ organs/memory organs/eyes organs/ears organs/heart -organs/build +organs/hands organs/pulse organs/insight organs/dna diff --git a/tests/test_nerves_rename_complete.py b/tests/test_nerves_rename_complete.py index c66be7e1..6658bafe 100644 --- a/tests/test_nerves_rename_complete.py +++ b/tests/test_nerves_rename_complete.py @@ -27,6 +27,8 @@ DELIBERATE = { # The organism's own record of the rename having happened. "ORGANISM.md", + # The Nerves docs page tells adopters the old repo/package names. + "docs/organs/nerves.md", # The back-compat alias table — old @-mentions in ~150 archived Mind prompts # must keep routing to the Nerves repo. "config/policy.yaml",