session: the uv-tool repair belongs to the hook, not the bootstrap - #363
Merged
Merged
Conversation
`repair_uv_tools` shipped in scripts/session_bootstrap.sh, which is what a MULTI-repo session runs — that session registers no SessionStart hook, so the bootstrap is its only door. A SINGLE-repo session is the mirror image: the hook fires and nothing calls the bootstrap. It got leg 2's breakage (the /usr/local/bin/python3 wrapper every uv tool env symlinks to) and none of the repair, so mypy, flake8, black, poetry and pyright all answered ModuleNotFoundError naming themselves, in the ordinary case. Moved to policy/session_start_hook.sh as leg 3b, called right after retool_uv_tools. Two paths reach the breakage, not one: leg 3 rebuilds a 3.11 tool and hands the new env the hijacked path, and leg 2 separately breaks every pre-existing tool env that already pointed there and that leg 3 skips. Running after leg 3 covers both, because leg 2 runs before it. Hardened for the move: the hook runs `set -euo pipefail` and the bootstrap only `set -u`, so two command substitutions would have aborted the session start, and a failing `ln -sfn` on an unwritable tools dir would have killed it outright. Every leg here is contractually non-fatal; these are now too. The bootstrap keeps both call sites, forwarding to the hook as a subprocess so the hook's `set -e` never leaks into a script that is a bootstrap and never a gate. Its --check leg is unchanged. Tests: the section-7 tests keep driving the bootstrap seam, which is now a forward — that is what proves the forward works. Four new: the leg through the hook, the healthy-env no-op, the unwritable-dir warning path, and an end-to-end run of the hook as the harness runs it, which fails on exactly this bug when the flow call is removed.
This was referenced Aug 27, 2026
Merged
Merged
Merged
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Two halves of the 2026-08-27 mobile-workflow pass that were, by construction, only half-rolled-out — the session that shipped them could attach only two of the four organs. Closes the second half here; the first half (Heart and Hands opting into the generated remote-session block) ships in their own PRs.
repair_uv_toolsshipped inscripts/session_bootstrap.sh. That is the wrong home. The bootstrap is what a multi-repo session runs, because that session registers no SessionStart hook and has no other door. A single-repo session is the mirror image: the hook fires, nothing calls the bootstrap. It got leg 2's breakage — the/usr/local/bin/python3wrapper that every uv tool env symlinks to — and none of the repair, somypy,flake8,black,poetryandpyrightall died withModuleNotFoundErrornaming themselves, in the ordinary single-repo case.Moved into
policy/session_start_hook.shas leg 3b, called right afterretool_uv_tools. Two paths reach that breakage, not one: leg 3 rebuilds a 3.11 tool and hands the new env the hijacked path, and leg 2 separately breaks every pre-existing tool env that already pointed there and that leg 3 skips (is_py312 … && continue). Running after leg 3 covers both, because leg 2 runs before it.API Changes
None — no public Python API is touched. Two shell contracts change, both internal to the session machinery:
policy/session_start_hook.shgains a--repair-uv-toolsargv seam (previously took no arguments) and callsrepair_uv_toolsin its main flow.scripts/session_bootstrap.shno longer definesrepair_uv_tools; its own--repair-uv-toolsseam and its trailing call now forward to the hook as subprocesses. The--checkleg is unchanged.See full details below.
Test Plan
python3 -m pytest -qin PyAutoMind — 271 passedassert '/tmp/…/session' == '/tmp/…/tools/widget'repos_sync.py --check --root <four-organ scope>— all 14 legs OKHardening the move required
The hook runs
set -euo pipefail; the bootstrap ranset -uonly. Three things would have failed the whole session start, silently, in a script whose contract is that every leg "degrades to a logged warning":base="$(… 2>/dev/null)"— a failing command substitution in an assignment aborts underset -eprefix="$(…)"— sameln -sfnitself, on an unwritable tools dir — now warns and continuesScope: this does not clean up the long tail
write_session_hooksiterates the whole manifest, so the hook is generated into 34 repos, not four. Thirty of those copies are a pass behind and two carry none at all — a state that predates this PR and that no gate sees, becausefirewall_gate.ymlchecks out exactly the four organs and skips absent repos. Heart sees it (manifest drift: session-start hooks (generated) — 34 mismatch(es)), CI does not.This PR regenerates the four organs — the set CI actually gates — and leaves the count where it found it. The propagation is filed separately as
draft/maintenance/organs/session_hook_reaches_only_four_of_thirty_four_repos.md, since it would mean PRs against every library, workspace, HowTo and assistant repo.Full API Changes (for automation & release notes)
Added
policy/session_start_hook.sh --repair-uv-tools— runs leg 3b alone, without a session start. HonoursPYAUTO_UV_TOOLS_DIRandPYAUTO_SESSION_VENV.repair_uv_tools()inpolicy/session_start_hook.sh(leg 3b), called from the main flow afterretool_uv_tools.Removed
repair_uv_tools()fromscripts/session_bootstrap.sh— moved, not deleted.Migration
bash PyAutoMind/scripts/session_bootstrap.sh --repair-uv-toolsstill works and still repairs; it now forwards to the hook.Closes part 2 of #360.
Generated by the PyAutoLabs agent workflow.