You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Two hygiene leftovers found by the 2026-08-19 release-board census (#239), both in PyAutoHands.
First, autohands/generate_release_notes.py carries a time-boxed ANNOUNCEMENT banner that expired 2026-07-24 — announcement_banner() now returns "" on every call. It is dead code that reads like live machinery, so it gets deleted rather than documented.
Second, bin/autohands help claims to be "the registry of what is a CLI verb" (AGENTS.md, fixed by #239), but a sweep of autohands/ finds 11 executable modules (if __name__ == "__main__") that help never mentions. Nothing enforces the claim, so it re-rots with the next module added. This task promotes the human-runnable ones to verbs, declares the rest internal, and adds a test that makes the registry an enforced invariant.
Plan
Delete the expired ANNOUNCEMENT dict, announcement_banner() and its call site from generate_release_notes.py.
Register nine autohands/ entrypoints as real CLI verbs in bin/autohands, each with an accurate help entry stating its actual input contract (they differ — cwd vs --root vs positional path) and where it runs in anger.
Declare the remaining modules internal in an explicit INTERNAL_MODULES list, and print them in autohands help so the registry is complete rather than partial.
Add tests/test_autohands_registry.py enforcing that every executable module is either a verb or allowlisted, that the allowlist has no ghosts, and that every verb has the SHORT_DESC + cmd_* + help_* trio the file's own header requires.
Update the now-false AGENTS.md clause that calls the navigator modules "not CLI verbs".
Out of scope: the ~30 stale remote branches (incl. origin/master, origin/release) — a destructive git sweep, run separately via /repo_cleanup rather than mixed into a PR diff.
Detailed implementation plan
Work Classification
Library — PyAutoHands source only, no workspace follow-up.
Affected Repositories
PyAutoHands (primary)
Branch Survey
Repository
Current Branch
Dirty?
./PyAutoHands
main
clean (1 commit behind origin/main — pull before branching)
worktree_check_conflict hands-hygiene-leftovers PyAutoHands → exit 0 (no conflict); worktree_list_claimed empty.
autohands/generate_release_notes.py: delete the ANNOUNCEMENT dict + preceding comment block (lines 23–36) and announcement_banner() (lines 38–46).
Delete the call site in generate_notes() (lines 200–204: banner = announcement_banner(repo) and the if banner: append block).
Drop from datetime import date only if nothing else in the file uses date (grep first).
tests/test_slack_release_notes.py: the LENS_BODY fixture embeds the banner markdown as a literal string and imports only slack_release_notes — deletion does not break it. Keep the blockquote line (it is the fixture's only > → mrkdwn case) but fix the now-stale comment at lines 17–18 describing the body as "what generate_release_notes.py produces (banner + …)".
2. Register the CLI verbs (bin/autohands)
Nine new verbs, each with a SHORT_DESC entry, a help_<name> heredoc and a cmd_<name> calling the existing _python_in_autohands helper (sets PYTHONPATH, inherits cwd unchanged — required by the workspace-root tools):
Verb
Section
Input contract
generate_markdown
Workspace operations
<project> [--only SUBSTR], run from workspace root (cwd)
regenerate_navigator
Workspace operations
[project] or NAVIGATOR_PROJECT env, run from workspace root
check_navigator
new Workspace guards
--root DIR (default .), --banners warn|fail, --ignore-file
check_search_memory
Workspace guards
--root DIR (required)
check_dataset_allowlist
Workspace guards
no args — operates on cwd
validate_env_profiles
Workspace guards
positional <workspace> path (not --root)
slack_release_notes
Release support
--version --result --run-url [--repo]
tombstone
Release support
[--out DIR] [--package NAME]
clone_seed
new Assistant seeding
<plan.json> [--push] [--out DIR]
New "# "-prefixed section headers: Workspace guards (CI-invoked; runnable locally) and Assistant seeding.
Each help_* body names where the tool runs in anger — navigator_check.yml, PyAutoHeart/.github/workflows/smoke-tests.yml:148, pre_build.sh:162, release.yml:588, publish_tombstone.yml — so a maintainer knows whether running it locally reproduces CI. Keep entries the length of the existing ones; minimal, not maximal.
navigator.py, build_util.py, env_config.py, result_collector.py, generate_autofit.py have no __main__ — libraries, stay internal.
3. Make help complete: declare the internal modules
Add an INTERNAL_MODULES array beside SUBCOMMAND_ORDER holding add_notebook_quotes (docstring→cell transform; __main__ is a debug aid), run_notebook (subprocess worker spawned by run.py), plus the five libraries above, each with a one-line reason. Print them in cmd_help's no-arg output as a trailing Internal modules (not CLI verbs): block.
4. Enforce it: tests/test_autohands_registry.py
New test module (stdlib + regex over the bin/autohands text; no bash execution) asserting:
Completeness — every autohands/*.py containing if __name__ == "__main__" is in SUBCOMMAND_ORDER or INTERNAL_MODULES; the failure message names the module and both options.
No stale allowlist — every INTERNAL_MODULES entry resolves to an existing autohands/<name>.py, so a rename or deletion cannot leave a ghost.
Verb contract — every non-"# " entry in SUBCOMMAND_ORDER has a SHORT_DESC[...], a cmd_<name>() and a help_<name>(). This is the file's own stated rule (bin/autohands lines 19–23) with nothing enforcing it today.
Disjointness — no name appears in both lists.
Shims (verify_install, url_check, watch/status/tick/fix) and bash subcommands (pre_build, bump_colab_urls, tag_and_merge) have no autohands/<name>.py; check 1 walks the filesystem only, so they are unaffected, and check 3 still covers them.
5. Update AGENTS.md
Lines 27–33 describe the navigator modules as "workflow-invoked, not CLI verbs" — false after step 2. One-sentence rewrite pointing at the enforced registry; no duplicated verb list (bin/autohands help stays the single source).
Key Files
autohands/generate_release_notes.py — expired banner mechanism + its call site
bin/autohands — SUBCOMMAND_ORDER, SHORT_DESC, new INTERNAL_MODULES, per-verb cmd_*/help_*, cmd_help output
tests/test_autohands_registry.py — new, the registry-completeness guard
tests/test_slack_release_notes.py — stale fixture comment only
Plus a real guard run against a live workspace (autohands check_navigator --root . --banners=warn from autolens_workspace), and a negative test for the new guard: temporarily add an autohands/zzz_probe.py with a __main__ block, confirm test_autohands_registry.pyfails, then delete it. A guard that never fires is not a guard.
Original Prompt
Click to expand starting prompt
Hands hygiene: expired announcement dead code, unregistered modules
Type: maintenance
Target: pyautohands
Repos:
PyAutoHands
Difficulty: small
Autonomy: safe
Priority: low
Status: formalised
Found by the 2026-08-19 release-board census (#239):
@PyAutoHands/autohands/generate_release_notes.py:23-46 carries an
ANNOUNCEMENT banner mechanism that expired 2026-07-24 — now dead code.
Delete it (delete the trap, don't document it).
Several modules exist but are not registered in the bin/autohands
dispatcher (navigator.py, check_navigator.py, regenerate_navigator.py,
generate_markdown.py, validate_env_profiles.py, check_search_memory.py,
check_dataset_allowlist.py). AGENTS.md prose was fixed (feat: release board — what shipped, one-tap prompts, Pages twin #239) to call them
workflow-invoked modules; decide per module whether it should become a CLI
verb or stay internal, and make bin/autohands help the complete registry.
~30 stale remote branches (incl. origin/master, origin/release,
near-duplicate feature names) — sweep via /repo_cleanup.
Overview
Two hygiene leftovers found by the 2026-08-19 release-board census (#239), both in PyAutoHands.
First,
autohands/generate_release_notes.pycarries a time-boxedANNOUNCEMENTbanner that expired 2026-07-24 —announcement_banner()now returns""on every call. It is dead code that reads like live machinery, so it gets deleted rather than documented.Second,
bin/autohands helpclaims to be "the registry of what is a CLI verb" (AGENTS.md, fixed by #239), but a sweep ofautohands/finds 11 executable modules (if __name__ == "__main__") thathelpnever mentions. Nothing enforces the claim, so it re-rots with the next module added. This task promotes the human-runnable ones to verbs, declares the rest internal, and adds a test that makes the registry an enforced invariant.Plan
ANNOUNCEMENTdict,announcement_banner()and its call site fromgenerate_release_notes.py.autohands/entrypoints as real CLI verbs inbin/autohands, each with an accurate help entry stating its actual input contract (they differ — cwd vs--rootvs positional path) and where it runs in anger.INTERNAL_MODULESlist, and print them inautohands helpso the registry is complete rather than partial.tests/test_autohands_registry.pyenforcing that every executable module is either a verb or allowlisted, that the allowlist has no ghosts, and that every verb has theSHORT_DESC+cmd_*+help_*trio the file's own header requires.AGENTS.mdclause that calls the navigator modules "not CLI verbs".Out of scope: the ~30 stale remote branches (incl.
origin/master,origin/release) — a destructive git sweep, run separately via/repo_cleanuprather than mixed into a PR diff.Detailed implementation plan
Work Classification
Library — PyAutoHands source only, no workspace follow-up.
Affected Repositories
Branch Survey
origin/main— pull before branching)worktree_check_conflict hands-hygiene-leftovers PyAutoHands→ exit 0 (no conflict);worktree_list_claimedempty.Suggested branch:
feature/hands-hygiene-leftoversWorktree root:
~/Code/PyAutoLabs-wt/hands-hygiene-leftovers/Implementation Steps
1. Delete the expired announcement banner
autohands/generate_release_notes.py: delete theANNOUNCEMENTdict + preceding comment block (lines 23–36) andannouncement_banner()(lines 38–46).generate_notes()(lines 200–204:banner = announcement_banner(repo)and theif banner:append block).from datetime import dateonly if nothing else in the file usesdate(grep first).tests/test_slack_release_notes.py: theLENS_BODYfixture embeds the banner markdown as a literal string and imports onlyslack_release_notes— deletion does not break it. Keep the blockquote line (it is the fixture's only>→ mrkdwn case) but fix the now-stale comment at lines 17–18 describing the body as "what generate_release_notes.py produces (banner + …)".2. Register the CLI verbs (
bin/autohands)Nine new verbs, each with a
SHORT_DESCentry, ahelp_<name>heredoc and acmd_<name>calling the existing_python_in_autohandshelper (setsPYTHONPATH, inherits cwd unchanged — required by the workspace-root tools):generate_markdown<project> [--only SUBSTR], run from workspace root (cwd)regenerate_navigator[project]orNAVIGATOR_PROJECTenv, run from workspace rootcheck_navigator--root DIR(default.),--banners warn|fail,--ignore-filecheck_search_memory--root DIR(required)check_dataset_allowlistvalidate_env_profiles<workspace>path (not--root)slack_release_notes--version --result --run-url [--repo]tombstone[--out DIR] [--package NAME]clone_seed<plan.json> [--push] [--out DIR]New
"# "-prefixed section headers:Workspace guards (CI-invoked; runnable locally)andAssistant seeding.Each
help_*body names where the tool runs in anger —navigator_check.yml,PyAutoHeart/.github/workflows/smoke-tests.yml:148,pre_build.sh:162,release.yml:588,publish_tombstone.yml— so a maintainer knows whether running it locally reproduces CI. Keep entries the length of the existing ones; minimal, not maximal.navigator.py,build_util.py,env_config.py,result_collector.py,generate_autofit.pyhave no__main__— libraries, stay internal.3. Make
helpcomplete: declare the internal modulesAdd an
INTERNAL_MODULESarray besideSUBCOMMAND_ORDERholdingadd_notebook_quotes(docstring→cell transform;__main__is a debug aid),run_notebook(subprocess worker spawned byrun.py), plus the five libraries above, each with a one-line reason. Print them incmd_help's no-arg output as a trailingInternal modules (not CLI verbs):block.4. Enforce it:
tests/test_autohands_registry.pyNew test module (stdlib + regex over the
bin/autohandstext; no bash execution) asserting:autohands/*.pycontainingif __name__ == "__main__"is inSUBCOMMAND_ORDERorINTERNAL_MODULES; the failure message names the module and both options.INTERNAL_MODULESentry resolves to an existingautohands/<name>.py, so a rename or deletion cannot leave a ghost."# "entry inSUBCOMMAND_ORDERhas aSHORT_DESC[...], acmd_<name>()and ahelp_<name>(). This is the file's own stated rule (bin/autohandslines 19–23) with nothing enforcing it today.Shims (
verify_install,url_check,watch/status/tick/fix) and bash subcommands (pre_build,bump_colab_urls,tag_and_merge) have noautohands/<name>.py; check 1 walks the filesystem only, so they are unaffected, and check 3 still covers them.5. Update
AGENTS.mdLines 27–33 describe the navigator modules as "workflow-invoked, not CLI verbs" — false after step 2. One-sentence rewrite pointing at the enforced registry; no duplicated verb list (
bin/autohands helpstays the single source).Key Files
autohands/generate_release_notes.py— expired banner mechanism + its call sitebin/autohands—SUBCOMMAND_ORDER,SHORT_DESC, newINTERNAL_MODULES, per-verbcmd_*/help_*,cmd_helpoutputtests/test_autohands_registry.py— new, the registry-completeness guardtests/test_slack_release_notes.py— stale fixture comment onlyAGENTS.md— the "not CLI verbs" clauseVerification
Plus a real guard run against a live workspace (
autohands check_navigator --root . --banners=warnfromautolens_workspace), and a negative test for the new guard: temporarily add anautohands/zzz_probe.pywith a__main__block, confirmtest_autohands_registry.pyfails, then delete it. A guard that never fires is not a guard.Original Prompt
Click to expand starting prompt