Overview
Heart's manifest_drift check is YELLOW: PyAutoMind/scripts/repos_sync.py --check reports check tenant firewall (organ code): 9 mismatch(es) — instance facts (satellite repo names) in Brain/Heart/Hands organ code outside the declared config surfaces. A deep-research pass (2026-08-17, three parallel agents over the design docs, CI surfaces, and every finding) rejected the naive allowlist-all-9 clear: the recorded decision rule is "derivable or arbitrary → refactor; genuine branded fact → declare the surface" (PyAutoMind/complete/2026/08/autohands-firewall-allowlist.md), and recurrence is structural because no CI anywhere runs the firewall check — all 9 findings merged through green PRs (2026-08-08 → 08-14). This task clears the 9 by the rule (Phase A) and adds PR-time gates so an allowlist patch is never needed reactively again (Phase B).
Plan
- Extract Heart's
smoke.py workspace table to Heart's declared config surface (config/repos.yaml), the established idiom from the July config extraction.
- Genericise five test files whose repo-name fixtures are verified non-load-bearing (synthetic names can never drift); derive the Hands test's literals from its already-parsed
SPECS.
- Allowlist only the two justified findings (net +1 entry / +1 token, vs +8 entries for the naive clear), with the mandated exactness/negative-probe audit.
- Add an
--only check selector to repos_sync.py, then a firewall gate step to Brain/Heart/Hands PR CI (and a Mind-side leg for allowlist edits), so drift fails in the PR that authors it.
- File the
repos_sync --write config-surface stamper (assessment §8-4, the design's own endgame) as a separate draft prompt — out of scope here.
Detailed implementation plan
Research grounding (why this shape)
- Design intent: the firewall exists for the config-diff fork model (
PyAutoMind/docs/pyautobrain/pyautoscientist_generalisation_assessment.md §8; shipped prose in PyAutoBrain/docs/adoption/config_surfaces.md). FIREWALL_ALLOWLIST reached its intended terminal state 2026-07-10 (72 files / 262 tokens — "docstring examples + test fixtures + workspace-root defaults only", complete/2026/07/pyautoscientist-4b.md) and has since accreted to 109 files / 430 tokens via six reactive patches. The header forbids growing it casually.
- Recurrence mechanics: Brain/Heart/Hands PR workflows are deliberately pytest-only; Mind's PR CI never executes
repos_sync.py (a green Mind PR is NOT evidence the firewall passes — recorded in complete/2026/08/autohands-firewall-allowlist.md); Heart's manifest_drift.py is exit-0, local-only, folded into one YELLOW line, and invisible to the cloud health run (readiness.py --cloud lists it as unobserved). All 9 findings postdate the 2026-07-18 clear and postdate each repo's PR CI.
- Forbidden shortcut (recorded, do not re-propose): teaching the checker to ignore comments/docstrings.
Affected Repositories
- PyAutoMind (primary — allowlist,
--only selector, check_heart extension, Mind CI leg)
- PyAutoBrain (3 test-fixture genericisations, tests.yml gate step)
- PyAutoHeart (smoke.py config extraction, 2 test genericisations, heart-tests.yml restructure + gate)
- PyAutoHands (test derivation, tests.yml restructure + gate) — see Coordination below
Branch Survey (2026-08-17)
| Repository |
Current Branch |
Dirty? |
| ./PyAutoMind |
main |
clean (workflow-state commits only) |
| ./PyAutoBrain |
main |
clean |
| ./PyAutoHeart |
main |
clean |
| ./PyAutoHands |
main |
clean — claimed by version-stamp-sync-guards (PyAutoHands#235, feature/version-stamp-sync-guards) |
Suggested branch: feature/tenant-firewall-drift-aug (each repo)
Coordination: the PyAutoHands leg (test_pre_build_staging.py + tests.yml) must either wait for #235 to merge or land on its own branch with a rebase check afterward — do not touch the version-stamp task's files (autohands/ version-stamp/sync-guard code).
Phase A — clear the 9 by the decision rule
PyAutoHeart/heart/smoke.py — extract, don't allowlist. WORKSPACES (WorkspaceSpec(key, directory, chain), ~lines 36-97) and IMPORT_NAMES (~line 99) are executable config (consumed for paths, PYTHONPATH, pip targets, import preflight, argparse choices). Move both to a smoke: block in PyAutoHeart/config/repos.yaml; load with a strict reader (KeyError on absence — twin precedents: heart/checks/version_skew.py workspace_library(), PyAutoHands/autohands/run_all.py _load_workspaces(); both from complete/2026/07/pyautoscientist-3b-config.md). Config YAML is firewall-exempt (check_tenant_firewall scans only .py/.sh). Reword the residual --root argparse help at smoke.py:591 from "PyAutoLabs organism root" to "organism root". Extend check_heart in PyAutoMind/scripts/repos_sync.py to validate the new smoke: block's repo names against the manifest (closes the same identity-unchecked gap version_skew: has).
- Genericise fixtures (all verified non-load-bearing; add a convention comment per the Aug 5 precedent so real names don't creep back):
PyAutoBrain/tests/test_worktree_conflict_guard.py — 7 tokens in active.md fixture strings; the awk in bin/worktree.sh does plain string compares, validates against no repo list → RepoA/RepoB. Keep the module docstring's two real drifted forms only if quoted as history (prefer rewording).
PyAutoBrain/tests/test_intake_dashboard.py (~110/121/132) — owner in a fake issue URL; the regex never inspects the owner → ExampleOrg.
PyAutoBrain/tests/test_profiling_conductor.py — fixture dir autolens_profiling; every _run passes --workspace explicitly → profiling_workspace (also proves the override is honoured).
PyAutoHeart/tests/test_smoke.py — already half-synthetic (demo_workspace, legacy_workspace); chain entries are used as dir names / ./name pip strings, preflight tests use empty chains → LibraryA/LibraryB; root tmp_path/"PyAutoLabs" → "organism".
PyAutoHeart/tests/test_release_run.py (~263) — per_project dict key is merged generically, never matched → demo_workspace.
PyAutoHands/tests/test_pre_build_staging.py — derive in-file. The file already parses SPECS from pre_build.sh precisely so fixtures cannot drift; replace the arbitrary literals (autolens_assistant 141, autolens_workspace 176/216, HowToFit/autofit_workspace 204-211) with SPECS[i][0] picks, and pyautobase = tmp_path/"PyAutoLabs" (61) → neutral name (pre_build.sh derives PYAUTOBASE from its own location).
- Allowlist the two justified in
PyAutoMind/scripts/repos_sync.py FIREWALL_ALLOWLIST:
- extend
"PyAutoBrain/agents/conductors/intake/_intake.py" with autofit_workspace (measured-noise docstring at ~890 — names+counts are the evidence for rejecting the file-spread threshold; the code itself already derives from the body map);
- add
"PyAutoBrain/tests/test_intake_reconcile_ranking.py": {PyAutoArray, PyAutoFit, PyAutoLabs, autofit_workspace, autolens_workspace} — assertions pin resolution against the live body map (_upstream_noise filtering requires real manifest names; slug == "PyAutoLabs/PyAutoFit").
- Run the exactness audit mandated by
complete/2026/08/autohands-firewall-allowlist.md (negative probes: a wrong token in these files must still fail).
Phase B — PR-time enforcement
repos_sync.py main(): add --only <check-label> (repeatable or single) so a caller can run one leg; today it's all-or-nothing (lines ~845-874). Exit 1 only on the selected legs' drift.
PyAutoBrain/.github/workflows/tests.yml: add a step python3 PyAutoMind/scripts/repos_sync.py --check --only "tenant firewall (organ code)" --root "$GITHUB_WORKSPACE". The workflow already checks out Brain at path: PyAutoBrain + PyAutoLabs/PyAutoMind at path: PyAutoMind — the exact layout --root needs; check_tenant_firewall skips organs not checked out, so it gates exactly Brain. (--only avoids the map-block legs reddening Brain PRs on unrelated Mind-side changes.)
PyAutoHeart/.github/workflows/heart-tests.yml and PyAutoHands/.github/workflows/tests.yml: restructure from bare root checkout to path: PyAutoHeart|PyAutoHands + PyAutoLabs/PyAutoMind sibling; move pip/pytest under working-directory:; add the same --only firewall step. (Hands' "only pytest" header is a preference, not an invariant — it already concedes one cross-repo hole.)
- Mind-side leg: new job/workflow in PyAutoMind PR CI, path-filtered to
scripts/repos_sync.py, checking out Mind + the three organs and running the firewall leg (full --check acceptable — absent repos early-return), so allowlist edits and over-grants are themselves verified.
Phase C — file, don't build
- Write
PyAutoMind/draft/feature/pyautomind/repos-sync-config-stamper.md: teach repos_sync --write to stamp organ config surfaces from the body map + per-organ policy (assessment §8-4: "the only real engineering in the whole plan"; removes hand-mirroring). Reference this issue.
Key Files
PyAutoMind/scripts/repos_sync.py — FIREWALL_ALLOWLIST (+1 entry, +1 token), check_heart smoke-block leg, --only selector
PyAutoHeart/config/repos.yaml — new smoke: block; PyAutoHeart/heart/smoke.py — table → strict loader
PyAutoBrain/tests/{test_worktree_conflict_guard,test_intake_dashboard,test_profiling_conductor}.py, PyAutoHeart/tests/{test_smoke,test_release_run}.py, PyAutoHands/tests/test_pre_build_staging.py — fixture genericisation/derivation
PyAutoBrain/.github/workflows/tests.yml, PyAutoHeart/.github/workflows/heart-tests.yml, PyAutoHands/.github/workflows/tests.yml, Mind PR CI — gates
Verification
python3 PyAutoMind/scripts/repos_sync.py --check --root <workspace-root> → check tenant firewall (organ code): OK, all other legs unchanged, exit 0.
- Negative probes: insert a bogus manifest name into a genericised test file and into an allowlisted file → the check must flag both; remove before commit.
pytest green in Mind, Brain, Heart, Hands (the genericised tests especially).
- Each amended workflow passes on its own PR — the gate step proving itself on the branch that adds it.
- Heart local:
python3 PyAutoHeart/heart/checks/manifest_drift.py → "identity in sync"; YELLOW reason drops from readiness after merge to the canonical mains (Heart grades canonical checkouts, not worktrees).
Original Prompt
Click to expand starting prompt
See PyAutoMind/active/tenant-firewall-drift-aug.md (moved from draft/maintenance/pyautomind/ on issue creation) — includes the user's original request verbatim and the research conclusions.
Overview
Heart's
manifest_driftcheck is YELLOW:PyAutoMind/scripts/repos_sync.py --checkreports check tenant firewall (organ code): 9 mismatch(es) — instance facts (satellite repo names) in Brain/Heart/Hands organ code outside the declared config surfaces. A deep-research pass (2026-08-17, three parallel agents over the design docs, CI surfaces, and every finding) rejected the naive allowlist-all-9 clear: the recorded decision rule is "derivable or arbitrary → refactor; genuine branded fact → declare the surface" (PyAutoMind/complete/2026/08/autohands-firewall-allowlist.md), and recurrence is structural because no CI anywhere runs the firewall check — all 9 findings merged through green PRs (2026-08-08 → 08-14). This task clears the 9 by the rule (Phase A) and adds PR-time gates so an allowlist patch is never needed reactively again (Phase B).Plan
smoke.pyworkspace table to Heart's declared config surface (config/repos.yaml), the established idiom from the July config extraction.SPECS.--onlycheck selector torepos_sync.py, then a firewall gate step to Brain/Heart/Hands PR CI (and a Mind-side leg for allowlist edits), so drift fails in the PR that authors it.repos_sync --writeconfig-surface stamper (assessment §8-4, the design's own endgame) as a separate draft prompt — out of scope here.Detailed implementation plan
Research grounding (why this shape)
PyAutoMind/docs/pyautobrain/pyautoscientist_generalisation_assessment.md§8; shipped prose inPyAutoBrain/docs/adoption/config_surfaces.md).FIREWALL_ALLOWLISTreached its intended terminal state 2026-07-10 (72 files / 262 tokens — "docstring examples + test fixtures + workspace-root defaults only",complete/2026/07/pyautoscientist-4b.md) and has since accreted to 109 files / 430 tokens via six reactive patches. The header forbids growing it casually.repos_sync.py(a green Mind PR is NOT evidence the firewall passes — recorded incomplete/2026/08/autohands-firewall-allowlist.md); Heart'smanifest_drift.pyis exit-0, local-only, folded into one YELLOW line, and invisible to the cloud health run (readiness.py--cloudlists it as unobserved). All 9 findings postdate the 2026-07-18 clear and postdate each repo's PR CI.Affected Repositories
--onlyselector,check_heartextension, Mind CI leg)Branch Survey (2026-08-17)
version-stamp-sync-guards(PyAutoHands#235, feature/version-stamp-sync-guards)Suggested branch:
feature/tenant-firewall-drift-aug(each repo)Coordination: the PyAutoHands leg (test_pre_build_staging.py + tests.yml) must either wait for #235 to merge or land on its own branch with a rebase check afterward — do not touch the version-stamp task's files (
autohands/version-stamp/sync-guard code).Phase A — clear the 9 by the decision rule
PyAutoHeart/heart/smoke.py— extract, don't allowlist.WORKSPACES(WorkspaceSpec(key, directory, chain), ~lines 36-97) andIMPORT_NAMES(~line 99) are executable config (consumed for paths, PYTHONPATH, pip targets, import preflight, argparse choices). Move both to asmoke:block inPyAutoHeart/config/repos.yaml; load with a strict reader (KeyError on absence — twin precedents:heart/checks/version_skew.pyworkspace_library(),PyAutoHands/autohands/run_all.py_load_workspaces(); both fromcomplete/2026/07/pyautoscientist-3b-config.md). Config YAML is firewall-exempt (check_tenant_firewallscans only.py/.sh). Reword the residual--rootargparse help at smoke.py:591 from "PyAutoLabs organism root" to "organism root". Extendcheck_heartinPyAutoMind/scripts/repos_sync.pyto validate the newsmoke:block's repo names against the manifest (closes the same identity-unchecked gapversion_skew:has).PyAutoBrain/tests/test_worktree_conflict_guard.py— 7 tokens in active.md fixture strings; the awk inbin/worktree.shdoes plain string compares, validates against no repo list →RepoA/RepoB. Keep the module docstring's two real drifted forms only if quoted as history (prefer rewording).PyAutoBrain/tests/test_intake_dashboard.py(~110/121/132) — owner in a fake issue URL; the regex never inspects the owner →ExampleOrg.PyAutoBrain/tests/test_profiling_conductor.py— fixture dirautolens_profiling; every_runpasses--workspaceexplicitly →profiling_workspace(also proves the override is honoured).PyAutoHeart/tests/test_smoke.py— already half-synthetic (demo_workspace,legacy_workspace); chain entries are used as dir names /./namepip strings, preflight tests use empty chains →LibraryA/LibraryB; roottmp_path/"PyAutoLabs"→"organism".PyAutoHeart/tests/test_release_run.py(~263) —per_projectdict key is merged generically, never matched →demo_workspace.PyAutoHands/tests/test_pre_build_staging.py— derive in-file. The file already parsesSPECSfrompre_build.shprecisely so fixtures cannot drift; replace the arbitrary literals (autolens_assistant141,autolens_workspace176/216,HowToFit/autofit_workspace204-211) withSPECS[i][0]picks, andpyautobase = tmp_path/"PyAutoLabs"(61) → neutral name (pre_build.shderives PYAUTOBASE from its own location).PyAutoMind/scripts/repos_sync.pyFIREWALL_ALLOWLIST:"PyAutoBrain/agents/conductors/intake/_intake.py"withautofit_workspace(measured-noise docstring at ~890 — names+counts are the evidence for rejecting the file-spread threshold; the code itself already derives from the body map);"PyAutoBrain/tests/test_intake_reconcile_ranking.py": {PyAutoArray, PyAutoFit, PyAutoLabs, autofit_workspace, autolens_workspace} — assertions pin resolution against the live body map (_upstream_noisefiltering requires real manifest names;slug == "PyAutoLabs/PyAutoFit").complete/2026/08/autohands-firewall-allowlist.md(negative probes: a wrong token in these files must still fail).Phase B — PR-time enforcement
repos_sync.py main(): add--only <check-label>(repeatable or single) so a caller can run one leg; today it's all-or-nothing (lines ~845-874). Exit 1 only on the selected legs' drift.PyAutoBrain/.github/workflows/tests.yml: add a steppython3 PyAutoMind/scripts/repos_sync.py --check --only "tenant firewall (organ code)" --root "$GITHUB_WORKSPACE". The workflow already checks out Brain atpath: PyAutoBrain+PyAutoLabs/PyAutoMindatpath: PyAutoMind— the exact layout--rootneeds;check_tenant_firewallskips organs not checked out, so it gates exactly Brain. (--onlyavoids the map-block legs reddening Brain PRs on unrelated Mind-side changes.)PyAutoHeart/.github/workflows/heart-tests.ymlandPyAutoHands/.github/workflows/tests.yml: restructure from bare root checkout topath: PyAutoHeart|PyAutoHands+PyAutoLabs/PyAutoMindsibling; move pip/pytest underworking-directory:; add the same--onlyfirewall step. (Hands' "only pytest" header is a preference, not an invariant — it already concedes one cross-repo hole.)scripts/repos_sync.py, checking out Mind + the three organs and running the firewall leg (full--checkacceptable — absent repos early-return), so allowlist edits and over-grants are themselves verified.Phase C — file, don't build
PyAutoMind/draft/feature/pyautomind/repos-sync-config-stamper.md: teachrepos_sync --writeto stamp organ config surfaces from the body map + per-organ policy (assessment §8-4: "the only real engineering in the whole plan"; removes hand-mirroring). Reference this issue.Key Files
PyAutoMind/scripts/repos_sync.py— FIREWALL_ALLOWLIST (+1 entry, +1 token),check_heartsmoke-block leg,--onlyselectorPyAutoHeart/config/repos.yaml— newsmoke:block;PyAutoHeart/heart/smoke.py— table → strict loaderPyAutoBrain/tests/{test_worktree_conflict_guard,test_intake_dashboard,test_profiling_conductor}.py,PyAutoHeart/tests/{test_smoke,test_release_run}.py,PyAutoHands/tests/test_pre_build_staging.py— fixture genericisation/derivationPyAutoBrain/.github/workflows/tests.yml,PyAutoHeart/.github/workflows/heart-tests.yml,PyAutoHands/.github/workflows/tests.yml, Mind PR CI — gatesVerification
python3 PyAutoMind/scripts/repos_sync.py --check --root <workspace-root>→check tenant firewall (organ code): OK, all other legs unchanged, exit 0.pytestgreen in Mind, Brain, Heart, Hands (the genericised tests especially).python3 PyAutoHeart/heart/checks/manifest_drift.py→ "identity in sync"; YELLOW reason drops from readiness after merge to the canonical mains (Heart grades canonical checkouts, not worktrees).Original Prompt
Click to expand starting prompt
See
PyAutoMind/active/tenant-firewall-drift-aug.md(moved fromdraft/maintenance/pyautomind/on issue creation) — includes the user's original request verbatim and the research conclusions.