diff --git a/repos.yaml b/repos.yaml index 6d4bd366..fb890469 100644 --- a/repos.yaml +++ b/repos.yaml @@ -47,42 +47,56 @@ categories: repos: # --- organs ----------------------------------------------------------- + # `public_role` is the curated copy shown in the public front-door organ + # tables (org profile + PyAutoScientist READMEs, generated by repos_sync); + # it falls back to `role` when absent. `front_door: true` adds a non-organ + # repo (Nerves/PyAutoConf) to that public set without making it a + # category:organ — the internal organism map (system_map) stays strict. PyAutoMind: github: PyAutoLabs/PyAutoMind category: organ organ: Mind role: "Intent, goals, priorities, workflow state; every task starts as a markdown prompt here." + public_role: "Where you lead — every piece of work starts here as a plain-English markdown file saying *what* to do." PyAutoBrain: github: PyAutoLabs/PyAutoBrain category: organ organ: Brain role: "Reasoning/orchestration layer; how work is decomposed and routed; the specialist agents." + public_role: "The reasoning layer that works out *how* — classifying, planning and routing each task through specialist agents." PyAutoBuild: github: PyAutoLabs/PyAutoBuild category: organ organ: Hands role: "Packaging, tagging, notebook generation, PyPI release execution." + public_role: "The executor that packages, tags and releases the libraries to PyPI, nightly." PyAutoHeart: github: PyAutoLabs/PyAutoHeart category: organ organ: Heart role: "Health/readiness — the authoritative \"is it safe to release?\" verdict." + public_role: "The health monitor whose GREEN/YELLOW/RED verdict is the authoritative \"is it safe to release?\" gate." PyAutoMemory: github: PyAutoLabs/PyAutoMemory category: organ organ: Memory role: "Long-term scientific/software/project knowledge (see science pointer below)." + public_role: "Long-term scientific knowledge — cross-linked literature wikis the agents consult." PyAutoGut: github: PyAutoLabs/PyAutoGut category: organ organ: Gut role: "Owns the lifecycle of condemned self-material (stale branches, stashes, dead code/tests): holds it as durable, recoverable git refs through a transit window and voids it on a sweep. The storage mirror of Memory (retention vs release)." + public_role: "The storage mirror of Memory — holds *condemned* material (stale branches, dead code) as recoverable git refs, then voids it on a sweep." # --- scientific libraries ---------------------------------------------- PyAutoConf: github: PyAutoLabs/PyAutoConf category: library + front_door: true + organ: Nerves role: "The Nerves — the configuration/serialization layer connecting workspace conventions to libraries (layered config, version handshake, test_mode); the sixth organ, delivered as the `autoconf` package." + public_role: "The configuration and serialization layer (`autoconf`) connecting the organism's conventions to every library." PyAutoFit: github: PyAutoLabs/PyAutoFit category: library diff --git a/scripts/repos_sync.py b/scripts/repos_sync.py index f3e2a174..81e2ff09 100644 --- a/scripts/repos_sync.py +++ b/scripts/repos_sync.py @@ -52,6 +52,8 @@ MAP_END = "" HISTORY_BEGIN = "" HISTORY_END = "" +ORGANS_BEGIN = "" +ORGANS_END = "" # The universal "never rewrite history" safety policy is single-sourced in a # markdown file (so it can be edited without touching this generator) and @@ -189,6 +191,45 @@ def system_map(categories, repos): return "\n".join(lines) +def public_organs(repos): + """The front-door organ set: every `category: organ` row (manifest order) + plus any repo flagged `front_door: true` (e.g. Nerves/PyAutoConf — a + library that is part of the organism's public self-presentation without + being a category:organ). This is a *superset* of the internal organism map + (`system_map`, strict category:organ per PyAutoBrain/ORGANISM.md); the two + are deliberately allowed to differ.""" + organs = [(n, r) for n, r in repos.items() if r["category"] == "organ"] + front = [(n, r) for n, r in repos.items() + if r.get("front_door") and r["category"] != "organ"] + return organs + front + + +def organ_public_table(repos, *, bold): + """The organ table for a public front-door README, a pure function of the + body map. `bold` bold-links the repo cell (the `.github` org-profile style) + vs a plain link (the PyAutoScientist README style). Role text is + `public_role` (the curated public copy) falling back to the terse manifest + `role`.""" + lines = ["| Organ | Repo | Role |", "|---|---|---|"] + for name, repo in public_organs(repos): + url = f"https://github.com/{repo['github']}" + link = f"[**{name}**]({url})" if bold else f"[{name}]({url})" + role = repo.get("public_role", repo["role"]) + lines.append(f"| {repo.get('organ', name)} | {link} | {role} |") + return "\n".join(lines) + + +# The public front-door docs that must list every organ. The two READMEs carry +# a generated table between ORGANS markers; the hub blurb is prose, so it is +# presence-checked (every organ name must appear) rather than regenerated. All +# are soft-skipped when the sibling repo is not checked out. +PUBLIC_TABLE_TARGETS = [ + (".github/profile/README.md", True), + ("PyAutoScientist/README.md", False), +] +HUB_BLURB = "pyautolabs.github.io/index.html" + + def replace_block(path, content, begin=MARK_BEGIN, end=MARK_END): text = path.read_text() if begin not in text or end not in text: @@ -358,6 +399,45 @@ def check_history_blocks(root, repos, hpol): # this runs cleanly in a partial/web checkout. +def check_public_tables(root, repos): + """Every front-door README's generated organ table must match the body map + (so a new organ can never silently drop out of the public front door). + Soft-skips a target that is not checked out.""" + problems = [] + for rel, bold in PUBLIC_TABLE_TARGETS: + path = root / rel + if not path.exists(): + continue + text = path.read_text() + if ORGANS_BEGIN not in text or ORGANS_END not in text: + problems.append( + f"{rel}: no repos_sync:organs marker block " + "(add the markers, then run --write)" + ) + elif extract_block(text, ORGANS_BEGIN, ORGANS_END) != \ + organ_public_table(repos, bold=bold): + problems.append( + f"{rel}: organ table stale — run " + "`python3 PyAutoMind/scripts/repos_sync.py --write`" + ) + return problems + + +def check_hub_blurb(root, repos): + """The hub's prose organism blurb is not regenerated (it is grammar, not a + table), but every organ name must appear in it. Soft-skips when absent.""" + path = root / HUB_BLURB + if not path.exists(): + return [] + text = path.read_text() + return [ + f"{HUB_BLURB}: organ '{repo.get('organ', name)}' missing from the " + "organism blurb" + for name, repo in public_organs(repos) + if repo.get("organ", name) not in text + ] + + def claude_md_is_pointer(text): """A CLAUDE.md counts as compliant iff it `@`-imports AGENTS.md on its own line (a real import that expands into context), not merely prose naming it.""" @@ -638,6 +718,9 @@ def main(): for name in repos: write_block(root / name / "AGENTS.md", hpol, HISTORY_BEGIN, HISTORY_END, required=False) + for rel, bold in PUBLIC_TABLE_TARGETS: + write_block(root / rel, organ_public_table(repos, bold=bold), + ORGANS_BEGIN, ORGANS_END, required=False) write_claude_md_pointers(root, repos) checks = { @@ -648,6 +731,8 @@ def main(): "tenant firewall (organ code)": check_tenant_firewall(root, repos), "organism-map blocks (generated)": check_map_blocks(root, repos, smap), "never-rewrite-history blocks (generated)": check_history_blocks(root, repos, hpol), + "public front-door organ tables (generated)": check_public_tables(root, repos), + "hub organism blurb (organs present)": check_hub_blurb(root, repos), "CLAUDE.md → AGENTS.md pointers": check_claude_md_pointers(root, repos), } drift = False