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
Ten copies of .github/scripts/run_smoke.py are vendored across the workspace
repos in three structurally different variants. Re-measured 2026-08-24: there is zero functional drift inside any variant — but that is the receipt for three
manual N-repo sweeps paid to restore it (#185 env-resolver fork, #226/#227
per-script timeout, the jupyter-guard fix), not evidence the copy-per-repo design
is safe. The HowTo tier needed none of those sweeps because it holds no logic.
This task collapses the other two variants onto that same shape: a PyAutoHands-owned
runner with per-repo config only.
It also closes the prompt's blocking question. The autolens_workspace_test
timeout/kill divergence — "the only place consolidation destroys behaviour" — was
already resolved by promotion into build_util (#226/#227 -> 52408a84);
all ten copies now import timeout_for and kill_group.
Plan
Record the decision the prompt gates on: the timeout/kill behaviour was promoted to everyone, in-tree, and no repo keeps a documented divergence.
Add an allowlist mode (--list <file>) to PyAutoHands autohands/run_python.py,
so an opt-in smoke_tests.txt workspace can use the same runner as the opt-out no_run.yaml HowTo repos.
Collapse the four *_workspace_test copies (198 lines) to HowToLens-shaped
delegators (~75 lines).
Promote the notebook leg — regenerate-and-retry recovery, the jupyter-missing
non-abort path, and the check ordering around the skip guard — into PyAutoHands,
then collapse the three user-workspace copies (356 lines).
Fix docs/internals.md, which is stale on its own inventory ("nine copies, five
distinct revisions - they have drifted"; omits HowToFit; the drift claim is false).
Detailed implementation plan
Work Classification
Library (PyAutoHands is the primary and owns both new capabilities); the seven
workspace collapses follow as one-file PRs once each phase's Hands PR has merged.
Worktree root
~/Code/PyAutoLabs-wt/smoke-runner-delegation/ (created later by /start_library)
Measured 2026-08-24 by git ls-remote (web-github session; no local worktrees).
Every repo below still carries the merged sweep branch claude/backport-per-script-timeout-r3w1sv, which is the evidence for the
promotion decision.
Skip-guard (step 1): done.is_clean_skip_exit appears in exactly the three
notebook-capable copies and nowhere else — correct, since the other seven never
shell out to jupyter.
_BUILD_DIR (step 4): done. autogalaxy_workspace is byte-identical to its siblings.
Timeout/kill (step 2): answered by promotion.TIMEOUT_SECS / BUILD_SCRIPT_TIMEOUT in all ten copies; timeout_for + kill_group imported
from build_util with guarded local fallbacks; _kill_group in zero copies.
The blocker is discovery model, not behaviour
run_python.py is opt-out: recursive discovery under a directory, minus config/build/no_run.yaml, with no notebook leg. Both other variants are opt-in allowlists (smoke_tests.txt, smoke_notebooks.txt). That mismatch —
not any behavioural divergence — is why the seven copies still carry logic. So
each phase is a PyAutoHands feature first, per-repo edits second.
autohands/run_python.py: add --list <file>. When given, read the allowlist
(strip blanks and # comments) and run exactly those paths relative to directory, instead of find_scripts_in_folder. should_skip against no_run.yaml still applies, so an allowlisted script that is also no_run-listed
is skipped with its reason recorded — do not let the allowlist override an
explicit exclusion. --report-dir semantics are unchanged.
Cover it in PyAutoHands' own test suite: allowlist honoured, ordering preserved, no_run intersection skipped-with-reason, missing allowlist file is a clear
error, absent flag leaves discovery untouched.
Replace each _test repo's .github/scripts/run_smoke.py with a delegator
modelled on HowToLens/.github/scripts/run_smoke.py: PROJECT, the build_util import with the sibling-checkout fallback, and a subprocess.run
into run_python.py with --list smoke_tests.txt --report-dir test-results. --report-dir is load-bearing (without it run_python.py never propagates
failure and the gate is vacuously green).
Correct docs/internals.md:183: ten copies, three variants, no live drift;
add HowToFit; drop the "five distinct revisions - they have drifted" claim.
Phase 2 — workspace (3 repos, 356 -> ~75 lines)
Promote the notebook leg into PyAutoHands. build_util.execute_notebook
already carries the skip guard and execute_notebooks_in_folder already
exists, so this is a notebook allowlist CLI leg plus the recovery path — not a
rewrite. What must move across intact from the workspace copy:
regenerate_notebook — regenerate the single failing notebook from its
source .py via py_to_notebook into a temp dir and retry once; the
on-disk notebooks/ tree is never modified.
JUPYTER_MISSING_RC = 127 — a missing jupyter is a per-entry failure,
never an abort and never a PASS.
The staged-copy-at-workspace-root trick in execute_notebook, so kernel cwd
is the repo root and relative dataset/ paths resolve.
Check ordering, which is load-bearing: jupyter-missing and TIMEOUT_RC
are both returned beforeis_clean_skip_exit is consulted, and a timeout
is never retried (retrying burns a second full cap for the same result).
Cover each of those four in PyAutoHands' test suite before any workspace is touched.
Replace the three workspace copies with delegators passing both allowlists
(smoke_tests.txt, smoke_notebooks.txt).
Phase 3 — HowTo (3 repos): no work. Already the end state.
Verification
Each phase's PyAutoHands PR merges first; the workspace PRs follow (library-first gate).
Per repo, the collapsed runner must produce the same PASS/FAIL/TIMEOUT verdicts
and the same === Smoke test summary: n/m passed === line on that repo's real
allowlist as the copy it replaces.
No repo loses behaviour: per-script timeout, process-group kill, exit code 124
distinct from a signal, notebook regenerate-and-retry, jupyter-missing non-abort.
Key Files
PyAutoHands/autohands/run_python.py — allowlist mode (phase 1), notebook leg (phase 2)
PyAutoMind/complete/2026/08/smoke-runner-jupyter-guard.md — the third sweep.
PyAutoMind/complete/2026/08/howto-smoke-all-tutorials.md — the opt-out rollout
that produced the HowTo delegator shape.
Original Prompt
Click to expand starting prompt
run_smoke.py: three runner variants across 10 repos, no sync mechanism
Type: maintenance
Target: ci
Repos:
@PyAutoHands
Difficulty: medium
Autonomy: supervised
Priority: normal
Status: draft
Filed: 2026-07-25 (backfilled from git)
Re-scoped 2026-08-05. The original finding below is preserved for history
but its two headline claims are now measured to be wrong: step 1 is already
done, and "9 copies in 5 revisions" conflates three structurally different
programs. Read the correction block first — it is the current statement of
the task.
Original finding (2026-07-25, during the notebook skip-guard fix)
Every workspace's PR smoke gate runs its own copy of .github/scripts/run_smoke.py. There are 9 copies across the workspace
repos in 5 distinct revisions — they have already drifted:
autofit_workspace + autolens_workspace share one revision; HowToLens +
HowToGalaxy + autofit_workspace_test share another; autogalaxy_workspace,
autogalaxy_workspace_test, autolens_workspace_test and autocti_workspace_test
are each unique. PyAutoHeart's reusable smoke-tests.yml deliberately leaves
the runner in the workspace, but nothing keeps the copies aligned.
Immediate consequence: PyAutoHands#198 taught the authoritative executor
(autohands/build_util.py::execute_notebook) to treat a clean SystemExit: 0
notebook exit as a PASS (the optional-dep skip-guard idiom), but the 9 smoke
copies still carry their own execute_notebook and keep reporting the
spurious FAIL until each adopts it. Adoption is ~2 lines per repo (they
already import env_config and build_util.py_to_notebook from PyAutoHands);
the exact snippet + full copy inventory is documented in
PyAutoHands docs/internals.md.
Correction (2026-08-05, measured during the jupyter-guard fix)
Measured across the checkouts, not inferred. There are 10 copies, not 9,
and they are three different programs, not five revisions of one:
Consequences for the original task, in order of how much they change it:
Step 1 is done, and was never applicable beyond three repos. The
skip-guard is adopted in all three notebook-capable copies. The other seven
have no execute_notebook at all — they never shell out to jupyter, so
there is no spurious FAIL for them to fix. The acceptance criterion "a
notebook exiting via the skip-guard passes every workspace's PR smoke gate"
is already met, because only three gates run notebooks.
The variants differ by feature, not only by drift.workspace_test
exposes load_smoke_scripts/run_one and no notebook machinery; autolens_workspace_test additionally carries TIMEOUT_SECS
(BUILD_SCRIPT_TIMEOUT) and a _kill_group process-group kill that no
other copy has. That is a capability, not staleness — a naive
"make them byte-identical" would delete it.
The HowTo tier is already the proposed end-state. Those three are
75-line delegators (PROJECT = "howtolens", straight into PyAutoHands build_util) — the thin-wrapper design step 2 asks whether to build
already exists in-tree as a working precedent to copy.
Real remaining drift inside the workspace variant is now two lines:
autofit_workspace and autolens_workspace are byte-identical; autogalaxy's
only divergence is an unused _BUILD_DIR intermediate variable. The
jupyter-guard fix (autolens_workspace#470) landed the identical patch in all
three, so it did not widen this.
Task (re-scoped)
Roll the 2-line skip-guard adoption across the copies.Done — verify
and close out, do not redo.
Decide the shared-module question per variant, not globally, using the
HowTo delegator as the reference shape:
Is one PyAutoHands-owned runner with per-repo config the right target, or
two (notebook-capable and script-only)?
Does autolens_workspace_test's timeout/kill behaviour get promoted to
everyone, or does that repo keep a documented divergence? Answer this
before writing any code — it is the only place consolidation destroys
behaviour.
Implement whichever shape is chosen, one PR per repo.
Drop the vestigial _BUILD_DIR line in autogalaxy_workspace if the
workspace variant is not being replaced wholesale.
Acceptance
A stated, written decision on the timeout/kill divergence — promoted or
documented-as-intentional — before any repo is touched.
Each variant is either a thin wrapper over a PyAutoHands-owned module, or
carries a documented reason why it diverges.
No repo loses behaviour it has today; autolens_workspace_test still
enforces its per-script timeout.
Re-measurement + decision (2026-08-24)
Re-measured from every repo's main before planning. The 2026-08-05
correction is itself now stale: its blocking question has been answered
in-tree, and the drift it measured is gone.
Ten copies, three variants, zero functional divergence inside any variant.
Every blocker the earlier correction named is closed:
The timeout/kill divergence was resolved by promotion, not documentation. timeout_for and kill_group now live in PyAutoHands autohands/build_util.py (fix: per-script timeout budget so full-dataset jax_grad scripts are measurable #226/fix: resolve the per-script timeout parent-side and keep timeout output #227 → 52408a84); all ten copies read BUILD_SCRIPT_TIMEOUT and import both with guarded local fallbacks. The
sweep branch claude/backport-per-script-timeout-r3w1sv is still present on
every affected repo. _kill_group exists in zero copies. Task step 2's
"answer this before writing any code" is therefore already answered:
promoted to everyone.
Step 1 (skip-guard) verified: is_clean_skip_exit appears in exactly the
three notebook-capable copies and nowhere else — correct, since the other
seven never shell out to jupyter.
Step 4 (_BUILD_DIR) is done — autogalaxy_workspace is byte-identical to
its two siblings.
The decision (task step 2, per variant)
Full delegation to a PyAutoHands-owned runner, staged. The HowTo tier is the
target shape and already exists in-tree.
The zero-drift measured above is not evidence the copy-per-repo design is safe —
it is the receipt for three manual N-repo sweeps that were needed to restore
it (env-resolver fork PyAutoHands#185, per-script timeout #226/#227, the
jupyter-guard fix). The HowTo tier needed none of those three sweeps,
precisely because it holds no logic: PROJECT plus a subprocess.run into autohands/run_python.py. That is the argument for consolidating, and it is a
maintenance-cost argument, not a drift argument.
The real blocker is discovery model, not behaviour: run_python.py is
opt-out (recursive discovery minus config/build/no_run.yaml) and has no
notebook leg, while both other variants are opt-in allowlists
(smoke_tests.txt, smoke_notebooks.txt). Consolidation is therefore a
PyAutoHands feature first and per-repo edits second.
Phase 1 — workspace_test (4 repos, 198 → ~75 lines).
Add an allowlist mode to autohands/run_python.py (--list <file>, taking
precedence over recursive discovery; no_run.yaml still applies). Then replace
each _test copy with a HowToLens-shaped delegator. Nothing is promoted that
isn't already in build_util — run_one is execute_script plus timeout_for/kill_group, all three already there.
Phase 2 — workspace (3 repos, 356 → ~75 lines).
Promote the notebook leg into PyAutoHands: the regenerate-from-source-and-retry
recovery, the JUPYTER_MISSING_RC = 127 non-abort path, and the ordering
invariant that a missing jupyter and a TIMEOUT_RC are both checked before is_clean_skip_exit. build_util.execute_notebook already carries the
skip-guard and execute_notebooks_in_folder already exists, so this is a
notebook-allowlist CLI leg plus the recovery, not a rewrite. Then collapse the
three workspace copies.
Phase 3 — HowTo (3 repos). No work. Already the end state; the audit
confirms it.
Also surfaced
PyAutoHands/docs/internals.md:183 is stale on its own inventory: it says
"nine copies, five distinct revisions — they have drifted", omits HowToFit
(which gained a copy with the opt-out HowTo smoke rollout), and the drift claim
is now false. Correct it as part of Phase 1.
Acceptance (restated against the decision)
The timeout/kill decision is written down above: promoted, with the
in-tree evidence. No repo keeps a documented divergence on that axis.
After Phase 2, every one of the ten copies is a thin wrapper over a
PyAutoHands-owned module; the only per-repo content is PROJECT and the
allowlist paths.
No repo loses behaviour: the per-script timeout, the process-group kill, the 124 timeout exit code, the notebook regenerate-and-retry recovery, and the
jupyter-missing non-abort path all survive as build_util/CLI behaviour and
are asserted by PyAutoHands' own test suite before any workspace is collapsed.
Overview
Ten copies of
.github/scripts/run_smoke.pyare vendored across the workspacerepos in three structurally different variants. Re-measured 2026-08-24: there is
zero functional drift inside any variant — but that is the receipt for three
manual N-repo sweeps paid to restore it (#185 env-resolver fork, #226/#227
per-script timeout, the jupyter-guard fix), not evidence the copy-per-repo design
is safe. The HowTo tier needed none of those sweeps because it holds no logic.
This task collapses the other two variants onto that same shape: a PyAutoHands-owned
runner with per-repo config only.
It also closes the prompt's blocking question. The
autolens_workspace_testtimeout/kill divergence — "the only place consolidation destroys behaviour" — was
already resolved by promotion into
build_util(#226/#227 ->52408a84);all ten copies now import
timeout_forandkill_group.Plan
promoted to everyone, in-tree, and no repo keeps a documented divergence.
--list <file>) to PyAutoHandsautohands/run_python.py,so an opt-in
smoke_tests.txtworkspace can use the same runner as the opt-outno_run.yamlHowTo repos.*_workspace_testcopies (198 lines) to HowToLens-shapeddelegators (~75 lines).
non-abort path, and the check ordering around the skip guard — into PyAutoHands,
then collapse the three user-workspace copies (356 lines).
docs/internals.md, which is stale on its own inventory ("nine copies, fivedistinct revisions - they have drifted"; omits HowToFit; the drift claim is false).
Detailed implementation plan
Work Classification
Library (PyAutoHands is the primary and owns both new capabilities); the seven
workspace collapses follow as one-file PRs once each phase's Hands PR has merged.
Worktree root
~/Code/PyAutoLabs-wt/smoke-runner-delegation/(created later by/start_library)Affected Repositories
run_python.pyallowlist mode, notebook leg,docs/internals.mdBranch Survey
Measured 2026-08-24 by
git ls-remote(web-github session; no local worktrees).Every repo below still carries the merged sweep branch
claude/backport-per-script-timeout-r3w1sv, which is the evidence for thepromotion decision.
Suggested branch:
feature/smoke-runner-delegationMeasured state (2026-08-24)
_testPROJECT =)Prior-task steps verified rather than redone:
is_clean_skip_exitappears in exactly the threenotebook-capable copies and nowhere else — correct, since the other seven never
shell out to
jupyter._BUILD_DIR(step 4): done. autogalaxy_workspace is byte-identical to its siblings.TIMEOUT_SECS/BUILD_SCRIPT_TIMEOUTin all ten copies;timeout_for+kill_groupimportedfrom
build_utilwith guarded local fallbacks;_kill_groupin zero copies.The blocker is discovery model, not behaviour
run_python.pyis opt-out: recursive discovery under a directory, minusconfig/build/no_run.yaml, with no notebook leg. Both other variants areopt-in allowlists (
smoke_tests.txt,smoke_notebooks.txt). That mismatch —not any behavioural divergence — is why the seven copies still carry logic. So
each phase is a PyAutoHands feature first, per-repo edits second.
Implementation Steps
Phase 1 —
workspace_test(4 repos, 198 -> ~75 lines)autohands/run_python.py: add--list <file>. When given, read the allowlist(strip blanks and
#comments) and run exactly those paths relative todirectory, instead offind_scripts_in_folder.should_skipagainstno_run.yamlstill applies, so an allowlisted script that is alsono_run-listedis skipped with its reason recorded — do not let the allowlist override an
explicit exclusion.
--report-dirsemantics are unchanged.no_runintersection skipped-with-reason, missing allowlist file is a clearerror, absent flag leaves discovery untouched.
_testrepo's.github/scripts/run_smoke.pywith a delegatormodelled on
HowToLens/.github/scripts/run_smoke.py:PROJECT, thebuild_utilimport with the sibling-checkout fallback, and asubprocess.runinto
run_python.pywith--list smoke_tests.txt --report-dir test-results.--report-diris load-bearing (without itrun_python.pynever propagatesfailure and the gate is vacuously green).
docs/internals.md:183: ten copies, three variants, no live drift;add HowToFit; drop the "five distinct revisions - they have drifted" claim.
Phase 2 —
workspace(3 repos, 356 -> ~75 lines)build_util.execute_notebookalready carries the skip guard and
execute_notebooks_in_folderalreadyexists, so this is a notebook allowlist CLI leg plus the recovery path — not a
rewrite. What must move across intact from the workspace copy:
regenerate_notebook— regenerate the single failing notebook from itssource
.pyviapy_to_notebookinto a temp dir and retry once; theon-disk
notebooks/tree is never modified.JUPYTER_MISSING_RC = 127— a missingjupyteris a per-entry failure,never an abort and never a PASS.
execute_notebook, so kernel cwdis the repo root and relative
dataset/paths resolve.TIMEOUT_RCare both returned before
is_clean_skip_exitis consulted, and a timeoutis never retried (retrying burns a second full cap for the same result).
(
smoke_tests.txt,smoke_notebooks.txt).Phase 3 — HowTo (3 repos): no work. Already the end state.
Verification
and the same
=== Smoke test summary: n/m passed ===line on that repo's realallowlist as the copy it replaces.
124distinct from a signal, notebook regenerate-and-retry, jupyter-missing non-abort.
Key Files
PyAutoHands/autohands/run_python.py— allowlist mode (phase 1), notebook leg (phase 2)PyAutoHands/autohands/build_util.py—timeout_for:25,kill_group:92,run_capped:104,py_to_notebook:157,should_skip:286,is_clean_skip_exit:325,execute_notebook:359,execute_notebooks_in_folder:472,execute_script:514,execute_scripts_in_folder:622PyAutoHands/docs/internals.md:183 — the stale copy inventoryHowToLens/.github/scripts/run_smoke.py— the 75-line reference shapeautolens_workspace/.github/scripts/run_smoke.py— the 356-line notebook variant being promotedautolens_workspace_test/.github/scripts/run_smoke.py— the 198-line script-only variantRelated history
wrapper unification as "a hygiene follow-up". This is that follow-up.
52408a84) —timeout_forpromotion, the decision thistask was gating on.
PyAutoMind/complete/2026/08/smoke-runner-jupyter-guard.md— the third sweep.PyAutoMind/complete/2026/08/howto-smoke-all-tutorials.md— the opt-out rolloutthat produced the HowTo delegator shape.
Original Prompt
Click to expand starting prompt
run_smoke.py: three runner variants across 10 repos, no sync mechanism
Type: maintenance
Target: ci
Repos:
Difficulty: medium
Autonomy: supervised
Priority: normal
Status: draft
Filed: 2026-07-25 (backfilled from git)
Original finding (2026-07-25, during the notebook skip-guard fix)
Every workspace's PR smoke gate runs its own copy of
.github/scripts/run_smoke.py. There are 9 copies across the workspacerepos in 5 distinct revisions — they have already drifted:
autofit_workspace + autolens_workspace share one revision; HowToLens +
HowToGalaxy + autofit_workspace_test share another; autogalaxy_workspace,
autogalaxy_workspace_test, autolens_workspace_test and autocti_workspace_test
are each unique. PyAutoHeart's reusable smoke-tests.yml deliberately leaves
the runner in the workspace, but nothing keeps the copies aligned.
Immediate consequence: PyAutoHands#198 taught the authoritative executor
(
autohands/build_util.py::execute_notebook) to treat a cleanSystemExit: 0notebook exit as a PASS (the optional-dep skip-guard idiom), but the 9 smoke
copies still carry their own
execute_notebookand keep reporting thespurious FAIL until each adopts it. Adoption is ~2 lines per repo (they
already import
env_configandbuild_util.py_to_notebookfrom PyAutoHands);the exact snippet + full copy inventory is documented in
PyAutoHands
docs/internals.md.Correction (2026-08-05, measured during the jupyter-guard fix)
Measured across the checkouts, not inferred. There are 10 copies, not 9,
and they are three different programs, not five revisions of one:
is_clean_skip_exitConsequences for the original task, in order of how much they change it:
skip-guard is adopted in all three notebook-capable copies. The other seven
have no
execute_notebookat all — they never shell out tojupyter, sothere is no spurious FAIL for them to fix. The acceptance criterion "a
notebook exiting via the skip-guard passes every workspace's PR smoke gate"
is already met, because only three gates run notebooks.
workspace_testexposes
load_smoke_scripts/run_oneand no notebook machinery;autolens_workspace_testadditionally carriesTIMEOUT_SECS(
BUILD_SCRIPT_TIMEOUT) and a_kill_groupprocess-group kill that noother copy has. That is a capability, not staleness — a naive
"make them byte-identical" would delete it.
75-line delegators (
PROJECT = "howtolens", straight into PyAutoHandsbuild_util) — the thin-wrapper design step 2 asks whether to buildalready exists in-tree as a working precedent to copy.
autofit_workspace and autolens_workspace are byte-identical; autogalaxy's
only divergence is an unused
_BUILD_DIRintermediate variable. Thejupyter-guard fix (autolens_workspace#470) landed the identical patch in all
three, so it did not widen this.
Task (re-scoped)
Roll the 2-line skip-guard adoption across the copies.Done — verifyand close out, do not redo.
HowTo delegator as the reference shape:
two (notebook-capable and script-only)?
autolens_workspace_test's timeout/kill behaviour get promoted toeveryone, or does that repo keep a documented divergence? Answer this
before writing any code — it is the only place consolidation destroys
behaviour.
_BUILD_DIRline in autogalaxy_workspace if theworkspace variant is not being replaced wholesale.
Acceptance
documented-as-intentional — before any repo is touched.
carries a documented reason why it diverges.
autolens_workspace_teststillenforces its per-script timeout.
Re-measurement + decision (2026-08-24)
Re-measured from every repo's
mainbefore planning. The 2026-08-05correction is itself now stale: its blocking question has been answered
in-tree, and the drift it measured is gone.
_testPROJECT =)Ten copies, three variants, zero functional divergence inside any variant.
Every blocker the earlier correction named is closed:
timeout_forandkill_groupnow live in PyAutoHandsautohands/build_util.py(fix: per-script timeout budget so full-dataset jax_grad scripts are measurable #226/fix: resolve the per-script timeout parent-side and keep timeout output #227 →52408a84); all ten copies readBUILD_SCRIPT_TIMEOUTand import both with guarded local fallbacks. Thesweep branch
claude/backport-per-script-timeout-r3w1svis still present onevery affected repo.
_kill_groupexists in zero copies. Task step 2's"answer this before writing any code" is therefore already answered:
promoted to everyone.
is_clean_skip_exitappears in exactly thethree notebook-capable copies and nowhere else — correct, since the other
seven never shell out to
jupyter._BUILD_DIR) is done — autogalaxy_workspace is byte-identical toits two siblings.
The decision (task step 2, per variant)
Full delegation to a PyAutoHands-owned runner, staged. The HowTo tier is the
target shape and already exists in-tree.
The zero-drift measured above is not evidence the copy-per-repo design is safe —
it is the receipt for three manual N-repo sweeps that were needed to restore
it (env-resolver fork PyAutoHands#185, per-script timeout #226/#227, the
jupyter-guard fix). The HowTo tier needed none of those three sweeps,
precisely because it holds no logic:
PROJECTplus asubprocess.runintoautohands/run_python.py. That is the argument for consolidating, and it is amaintenance-cost argument, not a drift argument.
The real blocker is discovery model, not behaviour:
run_python.pyisopt-out (recursive discovery minus
config/build/no_run.yaml) and has nonotebook leg, while both other variants are opt-in allowlists
(
smoke_tests.txt,smoke_notebooks.txt). Consolidation is therefore aPyAutoHands feature first and per-repo edits second.
Phase 1 —
workspace_test(4 repos, 198 → ~75 lines).Add an allowlist mode to
autohands/run_python.py(--list <file>, takingprecedence over recursive discovery;
no_run.yamlstill applies). Then replaceeach
_testcopy with a HowToLens-shaped delegator. Nothing is promoted thatisn't already in
build_util—run_oneisexecute_scriptplustimeout_for/kill_group, all three already there.Phase 2 —
workspace(3 repos, 356 → ~75 lines).Promote the notebook leg into PyAutoHands: the regenerate-from-source-and-retry
recovery, the
JUPYTER_MISSING_RC = 127non-abort path, and the orderinginvariant that a missing
jupyterand aTIMEOUT_RCare both checked beforeis_clean_skip_exit.build_util.execute_notebookalready carries theskip-guard and
execute_notebooks_in_folderalready exists, so this is anotebook-allowlist CLI leg plus the recovery, not a rewrite. Then collapse the
three workspace copies.
Phase 3 — HowTo (3 repos). No work. Already the end state; the audit
confirms it.
Also surfaced
PyAutoHands/docs/internals.md:183is stale on its own inventory: it says"nine copies, five distinct revisions — they have drifted", omits HowToFit
(which gained a copy with the opt-out HowTo smoke rollout), and the drift claim
is now false. Correct it as part of Phase 1.
Acceptance (restated against the decision)
in-tree evidence. No repo keeps a documented divergence on that axis.
PyAutoHands-owned module; the only per-repo content is
PROJECTand theallowlist paths.
124timeout exit code, the notebook regenerate-and-retry recovery, and thejupyter-missing non-abort path all survive as
build_util/CLI behaviour andare asserted by PyAutoHands' own test suite before any workspace is collapsed.