(The repo was renamed PyAutoBuild → PyAutoHands in 2026-07; the autohands
CLI/package name and the Build call-chain shorthand were kept — see
MIGRATION.md and PyAutoBrain/ORGANISM.md.)
PyAutoHands is the executor (the Hands) of the PyAuto release ecosystem:
packaging, tagging, notebook generation, and PyPI publication via release.yml.
PyAutoHands executes work on behalf of PyAutoBrain. It runs no
release-readiness checks of its own — that is PyAutoHeart's job.
The organs, boundaries and the Brain → Heart (gate) → Build (execute) call
chain are defined once in PyAutoBrain/ORGANISM.md. Build's side of it:
pure executor — it runs no readiness checks of its own and never
re-derives a gate decision; readiness is gated upstream by the Brain via
pyauto-heart readiness.
Release-readiness checking is no longer Build's job. The version-skew gate, the
deep verify_install suite, and URL hygiene all live in PyAutoHeart now;
autohands verify_install / autohands url_check / autohands watch|status| tick|fix are thin shims that delegate to pyauto-heart. Build keeps only the
executor primitives: the build/notebook pipeline (pre_build, generate*,
run_all / run*), the navigator catalogue and the workspace guards, tagging +
release (tag_and_merge, bump_colab_urls, release.yml), the release-notes
and Slack tooling (generate_release_notes, slack_release_notes), the release
board (board, published by release_board.yml), assistant seeding
(clone_seed), and repro_command. bin/autohands help is the complete
registry of autohands/ — every module there is a CLI verb or an
INTERNAL_MODULES entry, enforced by tests/test_autohands_registry.py, so
read help rather than listing verbs here. See docs/internals.md for the
pipeline detail.
See docs/internals.md for the build pipeline, workspace
folder structure, config files, and release.yml details. Read it when
changing the pipeline itself, not by default.
Three facts, all measured in a web/mobile container, where this file is loaded and little else is. They ride in every organ because a session may hold any subset of them — and the session that needs this most is the one holding several, which is exactly the session no hook fires in.
-
Bootstrap in the first turn, unconditionally — before the first test command, not as a remedy once something looks wrong:
bash PyAutoMind/scripts/session_bootstrap.sh # ~10s cold, ~1s warm bash PyAutoMind/scripts/session_bootstrap.sh --check # report onlyA session holding several organs registers no SessionStart hook — Claude Code reads project hooks from the project directory, which in that layout is the repos' parent, not a repo — so nothing has set this session up. It was once phrased as a remedy keyed to
No module named pytestor collectionImportErrors namingyaml; that symptom stopped appearing when the container image moved to Python 3.12, while the environment is still wrong in ways that read like a bad command rather than a stale session (pytest -n auto→unrecognized arguments: -n). The bootstrap also unshallows the clones: a remote session clones shallow, andgit merge-base --is-ancestorthen answers "not an ancestor" for a commit whose ancestry is merely absent — the answer the ship and close-out procedures act on when proving a branch merged. -
Then run the suite in parallel. 4 cores, subprocess-heavy suites, no single slow test: about 3.5x.
python3 -m pytest -q -n auto, withpytest-xdistsupplied by the bootstrap above. -
There is no
gh, and installing one does not help. A remote session reaches GitHub through themcp__github__*tools, already scoped to the session's repos.ghinstalls in two seconds and is a trap: it authenticates, then 403s every repo-scoped call, because the egress proxy serves neither the REST repo paths nor GraphQL beyond a pinned set of PR-review operations — a binary that looks healthy and fails everything that matters. It also defeats the surface probe, which keys offgh auth status. ReadPyAutoBrain/skills/GITHUB_ACCESS.mdat the top of any run that touches GitHub; it maps eachghoperation onto its MCP tool. Spell that path from the workspace root, as written: a multi-organ session is cwd'd at the repos' parent, so a bareskills/…reads as a missing file rather than a missing repo prefix.
Never rewrite pushed history on any repo with a remote — no git init over a
tracked repo, no force-push to main, no fresh-start "Initial commit", no
filter-repo / filter-branch / rebase -i on pushed branches. To get a
clean tree: git fetch origin && git reset --hard origin/main && git clean -fd.