Overview
The 2026-08-27 mobile-workflow pass shipped two things half-rolled-out, because the session that shipped them could attach only PyAutoMind and PyAutoBrain. Heart and Hands still carry hand-written remote-session text that is a pass behind (it names No module named pytest as the trigger — a symptom the canonical text records as having stopped appearing when the image moved to 3.12). And repair_uv_tools — the fix for uv tool envs that resolve into the session venv and die with ModuleNotFoundError naming themselves — sits in scripts/session_bootstrap.sh, which a single-repo session never calls; the hook is what that session runs.
Both halves landed in be0121ad and 33c81de6 (2026-08-27 18:14 UTC). Nothing has touched scripts/, policy/ or tests/ since, and there are no open PRs on the four organs.
Plan
- Add the
repos_sync:remote marker pair to PyAutoHeart/AGENTS.md and PyAutoHands/AGENTS.md, replacing their hand-written "Remote sessions" sections, so all four organs take the block from the generated source.
- Move
repair_uv_tools out of the bootstrap and into policy/session_start_hook.sh, beside retool_uv_tools which creates the condition — so the repair reaches a single-repo session, which runs the hook and never the bootstrap.
- Harden the moved function for the hook's
set -euo pipefail (the bootstrap runs set -u only, and two of its command substitutions would abort the hook as written).
- Regenerate the four organs' hook copies, keep the bootstrap's seam and its
--check leg working, and add a hook-level version of the uv-repair test.
- File a separate Mind prompt for the 30-repo hook-propagation backlog this task deliberately leaves (see "Scope" below).
Scope: the prompt's --check clause cannot be met, and why
The prompt says the hook "is generated into all four organs". It is generated into 34 repos — write_session_hooks iterates the whole manifest, not just organs. repos_sync.py --check is already red before this task touches anything:
check session-start hooks (generated): 34 mismatch(es)
✗ 'PyAutoMemory' … 'PyAutoFit' … 'autolens_workspace' … (30 stale copies)
✗ 'euclid_assistant': no .claude/hooks/session-start.sh
✗ 'admin_jammy': no .claude/hooks/session-start.sh
The four organs are clean; every other repo is a hook-pass behind, because the 2026-08-26 regeneration wave (PyAutoHeart 701b578, PyAutoHands d78ea54) also only saw four repos. No gate catches this: firewall_gate.yml checks out exactly Mind + Brain + Heart + Hands and skips absent repos, so the 30 stale copies are invisible to CI and visible only to a full local-workspace --check.
So this task regenerates the four organs — the set CI actually gates — and the acceptance bar becomes --check --root <four-organ worktree root> is clean. The 30-repo propagation is filed separately rather than folded in, since it would mean PRs against every library, workspace, HowTo and assistant repo.
Detailed implementation plan
Affected Repositories
- PyAutoMind (primary) — canonical hook, bootstrap, policy, tests
- PyAutoBrain — regenerated hook copy only
- PyAutoHeart — AGENTS.md markers + regenerated hook copy
- PyAutoHands — AGENTS.md markers + regenerated hook copy
PyAutoBrain is not listed in the prompt but must be claimed: it carries a generated hook copy that goes stale the moment the canonical hook changes, and firewall_gate.yml checks it.
Branch Survey
| Repository |
Current Branch |
Dirty? |
| ./PyAutoMind |
main |
clean |
| ./PyAutoBrain |
main |
clean |
| ./PyAutoHeart |
main |
clean |
| ./PyAutoHands |
main |
clean |
All four in sync with origin/main; worktree_check_conflict exits 0, no claims in active.md.
Suggested branch: feature/organ-remote-block-and-uv-hook-repair
Implementation Steps
1. Heart and Hands opt into the generated block
PyAutoHeart/AGENTS.md:49-70 — delete the hand-written ## Remote sessions: knock on the door first section, insert <!-- repos_sync:remote:begin --> / <!-- repos_sync:remote:end --> in its place, immediately above the existing repos_sync:history block. Mirrors PyAutoMind/AGENTS.md:104 and PyAutoBrain/AGENTS.md:163.
PyAutoHands/AGENTS.md:41-68 — same.
repos_sync.py --write fills both from policy/remote_sessions.md.
Deliberate loss: the canonical text carries no per-repo halves by design (repos_sync.py:128 module comment; test_the_canonical_text_carries_no_per_repo_numbers pins it). Heart's "641 tests / 7.6s / 2.7s", Hands' "406 tests / 27s / 13s" and Hands' .claude/session-python.txt bullet all go. Nothing is lost operationally — the hook reads that file at run time.
2. Move repair_uv_tools into the hook
- Move the function and its comment block from
scripts/session_bootstrap.sh:120-146 into policy/session_start_hook.sh as leg 3b, directly after retool_uv_tools (:327).
say → log.
set -e hardening (required). The hook runs set -euo pipefail; the bootstrap only set -u. Two assignments abort the whole hook as written — base="$("$VENV/bin/python" -c … 2>/dev/null)" and the post-repair prefix="$("$link" -c … 2>/dev/null)". Both get an || base="" / || prefix="" tail.
- Call it from the main flow (
:477) directly after retool_uv_tools, before point_pytest_at_venv.
- Add a
--repair-uv-tools argv seam beside the existing PYAUTO_SESSION_DEFINE_ONLY seam (:455).
Placement note for the moved comment: the bug reaches a tool env by two paths, not one. retool_uv_tools rebuilds 3.11 tools and hands them the hijacked /usr/local/bin/python3; point_system_default separately breaks every pre-existing tool env that already symlinked there and that retool skips (is_py312 … && continue). Running after retool_uv_tools covers both, because point_system_default runs earlier.
3. Bootstrap keeps its call, forwarding to the hook
session_bootstrap.sh:158-163 — the --repair-uv-tools seam becomes CLAUDE_CODE_REMOTE=true exec "$HOOK" --repair-uv-tools (after the existing [ -x "$HOOK" ] guard). Kept as a subprocess so the hook's set -euo pipefail never leaks into the bootstrap, which is contractually "never a gate".
session_bootstrap.sh:296 — the trailing call becomes the same subprocess invocation, non-fatal on failure. Still worth keeping: it covers the case where the hook's python leg exited early.
- The
--check leg is untouched.
4. Tests
tests/test_session_bootstrap.py — the existing _repair() helper still drives the bootstrap seam, which now proves the forward works end-to-end. test_a_tool_env_pointed_at_the_session_venv_is_repaired and test_a_healthy_tool_env_is_left_alone must stay green unchanged (they assert on "repointed widget", which log() still emits).
- Add the hook-level twin using the existing
_call_hook_function("repair_uv_tools", …) helper (:287) with PYAUTO_UV_TOOLS_DIR — no new seam needed for it.
- Add a test that the hook's main flow actually calls the repair. That is the bug: a single-repo session runs the hook, and nothing else.
5. Regenerate, scoped
--write from the full local workspace would write all 34 repos. Run it from the task worktree root instead, where only the four claimed repos exist — absent repos are skipped by design:
python3 PyAutoMind/scripts/repos_sync.py --write --root ~/Code/PyAutoLabs-wt/organ-remote-block-and-uv-hook-repair
python3 PyAutoMind/scripts/repos_sync.py --check --root ~/Code/PyAutoLabs-wt/organ-remote-block-and-uv-hook-repair
6. File the backlog prompt
New Mind draft under maintenance/organs/: the 30 stale hook copies + euclid_assistant and admin_jammy which carry none, and the fact that no gate sees them.
Key Files
PyAutoMind/policy/session_start_hook.sh — canonical hook; gains repair_uv_tools + the argv seam
PyAutoMind/scripts/session_bootstrap.sh — loses the function, keeps both call sites as hook invocations
PyAutoMind/policy/remote_sessions.md — canonical block, unchanged
PyAutoMind/scripts/repos_sync.py — unchanged (keeps firewall_gate.yml from triggering; it is path-filtered to this file)
PyAutoMind/tests/test_session_bootstrap.py — hook-level uv-repair coverage
PyAutoHeart/AGENTS.md, PyAutoHands/AGENTS.md — marker pairs replacing hand-written text
{PyAutoMind,PyAutoBrain,PyAutoHeart,PyAutoHands}/.claude/hooks/session-start.sh — regenerated
Work Classification
Library — organ code and generated copies. No workspace follow-up.
Worktree root
~/Code/PyAutoLabs-wt/organ-remote-block-and-uv-hook-repair/
Testing
python3 -m pytest -q in PyAutoMind (hook + bootstrap + repos_sync suites)
repos_sync.py --check --root <worktree root> clean on every leg
- Heart and Hands suites, since their AGENTS.md changes
Original Prompt
Click to expand starting prompt
# Two organs are missing this session's fixes, and the hook still ships the uv bug
Type: maintenance
Target: organs
Repos:
- PyAutoMind
- PyAutoHeart
- PyAutoHands
Difficulty: small
Autonomy: supervised
Priority: normal
Status: formalised
Filed: 2026-08-27
The 2026-08-27 mobile-workflow pass shipped two things that are, by
construction, only half-rolled-out. Both need a session with **all four organs
attached** — this one could not attach PyAutoHeart or PyAutoHands (`add_repo`
was refused), which is the same "a drift check is only as strong as the number
of repos your session can see" the last three passes each paid for once.
## 1. Opt Heart and Hands into the generated remote-session block
`PyAutoMind/policy/remote_sessions.md` is now the single source, written into
each repo's `AGENTS.md` between `<!-- repos_sync:remote:begin/end -->` by
`repos_sync.py --write`, with a drift check. Mind and Brain carry the markers;
Heart and Hands still carry their own hand-written text, which is already a pass
behind (it says "bootstrap if pytest misbehaves", a trigger that no longer
fires).
Add the markers to both, run `--write`, and confirm `--check` reports the block
for four repos rather than two. No gate order: `firewall_gate.yml` is
path-filtered to `scripts/repos_sync.py`, which this does not touch.
## 2. Promote the uv-tool repair from the bootstrap into the hook
`scripts/session_bootstrap.sh` gained `repair_uv_tools`: uv creates each tool
env's `bin/python` as a symlink to `/usr/local/bin/python3`, the hook replaces
that path with a wrapper that `exec`s the session venv, and the exec replaces
argv — so every tool env resolves `sys.prefix` to the venv and mypy, flake8,
black, poetry and pyright all die with `ModuleNotFoundError` naming themselves.
Measured 2026-08-27; `--check` called them all `3.12 OK` until the same commit
taught it to run each tool.
The fix belongs in `policy/session_start_hook.sh`, beside `retool_uv_tools`
which creates the condition — the hook is what a **single-repo** session runs,
and that session never calls the bootstrap. It was left in `scripts/` because
the hook is generated into all four organs and two of them could not be
attached: changing it here would have made `firewall_gate.yml` red on repos this
session could not regenerate.
So: move the function into the hook, regenerate all four copies, keep the
bootstrap's call (it is idempotent, and the bootstrap runs the hook anyway), and
keep the `--check` leg where it is.
## Done when
- `repos_sync.py --check` is clean with all four organs checked out, and the
remote-session block leg names four repos.
- A fresh remote session that runs only the SessionStart hook — no bootstrap
call — has a working `mypy`, `flake8` and `black`.
- The existing tests still pass, plus a hook-level version of
`test_a_tool_env_pointed_at_the_session_venv_is_repaired`.
Overview
The 2026-08-27 mobile-workflow pass shipped two things half-rolled-out, because the session that shipped them could attach only PyAutoMind and PyAutoBrain. Heart and Hands still carry hand-written remote-session text that is a pass behind (it names
No module named pytestas the trigger — a symptom the canonical text records as having stopped appearing when the image moved to 3.12). Andrepair_uv_tools— the fix for uv tool envs that resolve into the session venv and die withModuleNotFoundErrornaming themselves — sits inscripts/session_bootstrap.sh, which a single-repo session never calls; the hook is what that session runs.Both halves landed in
be0121adand33c81de6(2026-08-27 18:14 UTC). Nothing has touchedscripts/,policy/ortests/since, and there are no open PRs on the four organs.Plan
repos_sync:remotemarker pair toPyAutoHeart/AGENTS.mdandPyAutoHands/AGENTS.md, replacing their hand-written "Remote sessions" sections, so all four organs take the block from the generated source.repair_uv_toolsout of the bootstrap and intopolicy/session_start_hook.sh, besideretool_uv_toolswhich creates the condition — so the repair reaches a single-repo session, which runs the hook and never the bootstrap.set -euo pipefail(the bootstrap runsset -uonly, and two of its command substitutions would abort the hook as written).--checkleg working, and add a hook-level version of the uv-repair test.Scope: the prompt's
--checkclause cannot be met, and whyThe prompt says the hook "is generated into all four organs". It is generated into 34 repos —
write_session_hooksiterates the whole manifest, not just organs.repos_sync.py --checkis already red before this task touches anything:The four organs are clean; every other repo is a hook-pass behind, because the 2026-08-26 regeneration wave (PyAutoHeart
701b578, PyAutoHandsd78ea54) also only saw four repos. No gate catches this:firewall_gate.ymlchecks out exactly Mind + Brain + Heart + Hands and skips absent repos, so the 30 stale copies are invisible to CI and visible only to a full local-workspace--check.So this task regenerates the four organs — the set CI actually gates — and the acceptance bar becomes
--check --root <four-organ worktree root>is clean. The 30-repo propagation is filed separately rather than folded in, since it would mean PRs against every library, workspace, HowTo and assistant repo.Detailed implementation plan
Affected Repositories
PyAutoBrain is not listed in the prompt but must be claimed: it carries a generated hook copy that goes stale the moment the canonical hook changes, and
firewall_gate.ymlchecks it.Branch Survey
All four in sync with
origin/main;worktree_check_conflictexits 0, no claims inactive.md.Suggested branch:
feature/organ-remote-block-and-uv-hook-repairImplementation Steps
1. Heart and Hands opt into the generated block
PyAutoHeart/AGENTS.md:49-70— delete the hand-written## Remote sessions: knock on the door firstsection, insert<!-- repos_sync:remote:begin -->/<!-- repos_sync:remote:end -->in its place, immediately above the existingrepos_sync:historyblock. MirrorsPyAutoMind/AGENTS.md:104andPyAutoBrain/AGENTS.md:163.PyAutoHands/AGENTS.md:41-68— same.repos_sync.py --writefills both frompolicy/remote_sessions.md.Deliberate loss: the canonical text carries no per-repo halves by design (
repos_sync.py:128module comment;test_the_canonical_text_carries_no_per_repo_numberspins it). Heart's "641 tests / 7.6s / 2.7s", Hands' "406 tests / 27s / 13s" and Hands'.claude/session-python.txtbullet all go. Nothing is lost operationally — the hook reads that file at run time.2. Move
repair_uv_toolsinto the hookscripts/session_bootstrap.sh:120-146intopolicy/session_start_hook.shas leg 3b, directly afterretool_uv_tools(:327).say→log.set -ehardening (required). The hook runsset -euo pipefail; the bootstrap onlyset -u. Two assignments abort the whole hook as written —base="$("$VENV/bin/python" -c … 2>/dev/null)"and the post-repairprefix="$("$link" -c … 2>/dev/null)". Both get an|| base=""/|| prefix=""tail.:477) directly afterretool_uv_tools, beforepoint_pytest_at_venv.--repair-uv-toolsargv seam beside the existingPYAUTO_SESSION_DEFINE_ONLYseam (:455).Placement note for the moved comment: the bug reaches a tool env by two paths, not one.
retool_uv_toolsrebuilds 3.11 tools and hands them the hijacked/usr/local/bin/python3;point_system_defaultseparately breaks every pre-existing tool env that already symlinked there and thatretoolskips (is_py312 … && continue). Running afterretool_uv_toolscovers both, becausepoint_system_defaultruns earlier.3. Bootstrap keeps its call, forwarding to the hook
session_bootstrap.sh:158-163— the--repair-uv-toolsseam becomesCLAUDE_CODE_REMOTE=true exec "$HOOK" --repair-uv-tools(after the existing[ -x "$HOOK" ]guard). Kept as a subprocess so the hook'sset -euo pipefailnever leaks into the bootstrap, which is contractually "never a gate".session_bootstrap.sh:296— the trailing call becomes the same subprocess invocation, non-fatal on failure. Still worth keeping: it covers the case where the hook's python leg exited early.--checkleg is untouched.4. Tests
tests/test_session_bootstrap.py— the existing_repair()helper still drives the bootstrap seam, which now proves the forward works end-to-end.test_a_tool_env_pointed_at_the_session_venv_is_repairedandtest_a_healthy_tool_env_is_left_alonemust stay green unchanged (they assert on"repointed widget", whichlog()still emits)._call_hook_function("repair_uv_tools", …)helper (:287) withPYAUTO_UV_TOOLS_DIR— no new seam needed for it.5. Regenerate, scoped
--writefrom the full local workspace would write all 34 repos. Run it from the task worktree root instead, where only the four claimed repos exist — absent repos are skipped by design:6. File the backlog prompt
New Mind draft under
maintenance/organs/: the 30 stale hook copies +euclid_assistantandadmin_jammywhich carry none, and the fact that no gate sees them.Key Files
PyAutoMind/policy/session_start_hook.sh— canonical hook; gainsrepair_uv_tools+ the argv seamPyAutoMind/scripts/session_bootstrap.sh— loses the function, keeps both call sites as hook invocationsPyAutoMind/policy/remote_sessions.md— canonical block, unchangedPyAutoMind/scripts/repos_sync.py— unchanged (keepsfirewall_gate.ymlfrom triggering; it is path-filtered to this file)PyAutoMind/tests/test_session_bootstrap.py— hook-level uv-repair coveragePyAutoHeart/AGENTS.md,PyAutoHands/AGENTS.md— marker pairs replacing hand-written text{PyAutoMind,PyAutoBrain,PyAutoHeart,PyAutoHands}/.claude/hooks/session-start.sh— regeneratedWork Classification
Library — organ code and generated copies. No workspace follow-up.
Worktree root
~/Code/PyAutoLabs-wt/organ-remote-block-and-uv-hook-repair/Testing
python3 -m pytest -qin PyAutoMind (hook + bootstrap + repos_sync suites)repos_sync.py --check --root <worktree root>clean on every legOriginal Prompt
Click to expand starting prompt