From 6fc1a36a87606423ad40c41049955f11d337104c Mon Sep 17 00:00:00 2001
From: Claude
Date: Mon, 24 Aug 2026 01:43:31 +0000
Subject: [PATCH] =?UTF-8?q?prompt:=20ship=20smoke-runner-delegation=20(#26?=
=?UTF-8?q?0)=20=E2=86=92=20complete?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
All ten vendored .github/scripts/run_smoke.py copies are now thin delegators
over the PyAutoHands runners. 2085 lines -> 1127, and most of what remains is
the docstring explaining why each file delegates.
Merged, each verified from the CI log rather than the green tick:
PyAutoHands#261 --list opt-in lists + the notebook leg
PyAutoHands#262 allowlist authoritative (the 13-script finding)
PyAutoHands#263 regenerate_notebook resolves by relative path
autolens_workspace_test#273, autogalaxy_workspace_test#111,
autofit_workspace_test#90, autocti_workspace_test#16 (198 -> 77)
autofit_workspace#147, autogalaxy_workspace#226,
autolens_workspace#500 (356 -> 119)
The record keeps the two findings that mattered most: the no_run-wins rule
shipped in #261 would have silently dropped 13 scripts from smoke coverage with
CI still green, and regenerate_notebook's bare-filename lookup made the
stale-notebook recovery dead for every real (nested) notebook. Both were caught
by measuring the real repos before writing a delegator, and both are now pinned
by tests that were negative-tested against the old behaviour.
Co-Authored-By: Claude Opus 5
Claude-Session: https://claude.ai/code/session_01UpSFum81Jeq9KZ9wdKtaeZ
---
active.md | 49 --------
.../2026/08/smoke-runner-delegation.md | 115 ++++++++++++++++++
complete/index.md | 3 +-
dashboard.html | 17 ++-
dashboard.md | 20 +--
5 files changed, 131 insertions(+), 73 deletions(-)
rename active/run_smoke_copy_drift.md => complete/2026/08/smoke-runner-delegation.md (60%)
diff --git a/active.md b/active.md
index fccf3fc3..fb25a165 100644
--- a/active.md
+++ b/active.md
@@ -13,52 +13,3 @@
- registered: 2026-08-19 by the wake_up session — the issuing session (claude/autofit-priors-messages-audit-ylvenv)
filed the prompt + issue but not this entry, tripping Lifecycle Drift on main.
- repos-none-claimed: this entry claims NO repos — one line deliberately, not 2-space bullets.
-
-## smoke-runner-delegation
-- issue: https://github.com/PyAutoLabs/PyAutoHands/issues/260 (issued 2026-08-24)
-- issued: 2026-08-24
-- prompt: active/run_smoke_copy_drift.md
-- status: library-dev
-- worktree: ~/Code/PyAutoLabs-wt/smoke-runner-delegation
-- decision: the prompt's blocking question is ANSWERED before any repo is touched —
- the timeout/kill behaviour was PROMOTED to everyone (PyAutoHands#226/#227 → 52408a84;
- all 10 copies import timeout_for + kill_group). Shape chosen: full delegation to a
- PyAutoHands-owned runner, staged. Phase 1 = --list allowlist mode + the 4 *_workspace_test
- copies; phase 2 = notebook leg + the 3 workspace copies; phase 3 = HowTo, no work.
-- measured: 2026-08-24, from every repo's main — 10 copies, 3 variants, ZERO functional
- drift inside any variant (workspace 356L byte-identical; workspace_test 198L comment-only;
- HowTo 75L differ by PROJECT alone). Prompt steps 1 and 4 verified already done.
-- registered: 2026-08-24 by the start_dev session (claude/smoke-copy-drift-ci-docs-ozntvv);
- worktree_check_conflict clean (exit 0) for all 8 repos.
-- status-detail: HANDS LEGS MERGED, workspace collapses not started. The task stays
- active deliberately — issue #260 scopes seven workspace PRs that have zero commits,
- so this is not complete and must not be recorded as such.
-- library-pr: https://github.com/PyAutoLabs/PyAutoHands/pull/261 MERGED 2026-08-24 → c0e2e53
- (both Hands legs: phase 1 `--list` opt-in script lists on run_python.py; phase 2
- the matching notebook leg on run.py — `--list`, `--no-write-back`, `--retry-from`).
- 16 new tests; suite 388 passed / 5 skipped / 0 failed; CI green on all three
- matrix legs (3.12/3.13/3.14) plus the tenant-firewall step in each.
-- mind-pr: https://github.com/PyAutoLabs/PyAutoMind/pull/280 MERGED 2026-08-24 → 0cd8544a
-- phase-2 findings (these changed the plan, recorded so the workspace PRs inherit them):
- - run_notebook.py writes executed outputs back IN PLACE. Correct for generation
- (the outputs are the product), wrong for a PR gate, which must not dirty the
- tree it tests. Hence `--no-write-back`; it supersedes the workspace copy's
- staged-copy-at-root trick rather than porting it, since the kernel cwd is
- already pinned to the repo root.
- - JUPYTER_MISSING_RC did NOT need promoting. It exists because the workspace copy
- shelled out to a bare `jupyter`; execute_notebook invokes
- `sys.executable run_notebook.py`, so the abort-with-no-summary failure mode is
- structurally absent. One promotion item dissolved on inspection.
-- remaining: the 7 workspace collapses (4 *_workspace_test 198L → ~75L, then 3 workspace
- 356L → ~75L), each a one-file PR modelled on HowToLens's delegator. UNBLOCKED as of
- 2026-08-24 — the library-first gate cleared when PyAutoHands#261 merged. Phase 3
- (HowTo ×3) needs no work; it is already the end state.
- The delegator invocations these PRs should use:
- *_workspace_test: run_python.py scripts --list smoke_tests.txt
- --report-dir test-results
- workspace: the above, plus run.py notebooks
- --list smoke_notebooks.txt --no-write-back --retry-from scripts
- `--report-dir` is load-bearing: without it run_python.py never propagates failure
- and the gate is vacuously green.
-- repos:
- - PyAutoHands (branch claude/smoke-copy-drift-ci-docs-ozntvv)
diff --git a/active/run_smoke_copy_drift.md b/complete/2026/08/smoke-runner-delegation.md
similarity index 60%
rename from active/run_smoke_copy_drift.md
rename to complete/2026/08/smoke-runner-delegation.md
index 9d77d38b..9b3e42f7 100644
--- a/active/run_smoke_copy_drift.md
+++ b/complete/2026/08/smoke-runner-delegation.md
@@ -1,3 +1,118 @@
+Ten repos each carried their own `.github/scripts/run_smoke.py` — the file
+PyAutoHeart's reusable `smoke-tests.yml` invokes from inside each workspace, so
+one must exist per repo. What was duplicated was not the file's existence but
+its **driver loop**: read the allowlist, resolve per-entry env, spawn a
+subprocess under the timeout cap, classify PASS/FAIL/TIMEOUT, accumulate, print
+the summary, exit non-zero. Seven repos held that loop; the three HowTo repos
+were already thin shims.
+
+All ten are now shims. 2085 lines became 1127, and 1127 is mostly prose: of
+`autolens_workspace_test`'s 77 lines, 32 are the docstring explaining why it
+delegates.
+
+## The argument, which was not "they have drifted"
+
+The prompt was filed on a drift finding. Re-measured 2026-08-24 from every
+repo's `main`, there was **zero functional drift inside any variant** — the
+three workspace copies byte-identical, the four `_test` copies differing in two
+docstring lines, the three HowTo copies in `PROJECT` alone.
+
+That is not evidence the design was safe. It is the receipt for three manual
+N-repo sweeps that had been paid to restore it: the env-resolver fork (#185),
+the per-script timeout and process-group kill (#226/#227), and the jupyter
+guard. The HowTo tier needed **none** of the three, precisely because it holds
+no logic. The case for collapsing is maintenance cost, not drift.
+
+The prompt's blocking question — "does autolens_workspace_test's timeout/kill
+get promoted, or keep a documented divergence?" — was already answered by
+promotion in #226/#227. All ten copies imported `timeout_for` and `kill_group`
+before this task started; `_kill_group` existed in zero.
+
+## The real obstacle was discovery model, not behaviour
+
+The shared runners were opt-out (recursive discovery minus `no_run.yaml`); the
+seven logic-bearing copies are opt-in allowlists (`smoke_tests.txt`,
+`smoke_notebooks.txt`). That mismatch is why they could not delegate. So each
+phase was a PyAutoHands feature first and per-repo edits second.
+
+## Two bugs found by measuring before writing
+
+**1. The `no_run`-wins rule would have deleted 13 scripts from smoke coverage
+(#262).** #261 shipped "an explicit exclusion is the more specific intent, so
+`no_run` wins over the allowlist". Measured against the real repos, that rule
+would have silently skipped 9 scripts in autogalaxy_workspace_test, 2 in
+autolens_workspace_test, 1 in autofit_workspace and 1 in autolens_workspace —
+every one of which runs in smoke today, since the vendored runners never opened
+`no_run.yaml` at all.
+
+The rule was wrong because it conflated two policies for two different runs:
+`no_run.yaml` governs the release mega-run and notebook generation, the
+allowlist governs the PR gate, and a script legitimately appears in both. The
+failure mode is the dangerous kind — **CI would have stayed green**, because a
+skipped script is not a failure. Corrected to allowlist-authoritative before any
+workspace was touched, and confirmed in production afterwards: autogalaxy's nine
+all ran and passed, as did autolens's two and autofit's `searches/mcmc` in both
+its script and notebook forms.
+
+Same PR fixed a second blocker: `autocti_workspace_test` is the only workspace
+with no `config/build/no_run.yaml`, and the autohands-level fallback path does
+not exist either, so both runners crashed with `FileNotFoundError` before
+running anything.
+
+**2. `regenerate_notebook` resolved the source by bare filename (#263).** It
+looked up `scripts_dir / nb_path.name`, dropping the subdirectory, so
+`notebooks/imaging/model_fit.ipynb` searched for `scripts/model_fit.py`. Every
+workspace notebook is in a subdirectory, so the stale-notebook recovery was dead
+on arrival — and a bare filename can find the *wrong* source when two topics
+share a name. The existing tests missed it because their fixture was **flat**,
+which is the one layout where the bug is invisible. The replacement tests use a
+nested notebook plus a decoy script at the scripts root, and were negative-tested
+against the old resolution.
+
+## What was proven rather than assumed
+
+- **Env resolution is identical.** The old runners passed a *relative* script
+ path to `build_env_for_script`; the shared runners pass an *absolute* one. All
+ 128 listed scripts across all seven repos were resolved both ways and diffed:
+ 0 differences, in env and in args. Pattern matching is substring/stem based so
+ path form is irrelevant, and no profile pattern collides with the absolute
+ prefix.
+- No profile sets per-script `args`, so the shared runners' extra-args support
+ changes nothing.
+- Every allowlist entry resolves to a real file, and every listed notebook's
+ source script exists for the retry path.
+- The two-leg exit code is the worst of both, verified in all three
+ pass/fail combinations — a failing notebook cannot be masked by passing
+ scripts.
+- **Every merge was verified from the CI log, not the green tick**, because a
+ coverage regression here is indistinguishable from a pass. Each repo's
+ "Running N listed scripts/notebooks" was checked against its allowlist:
+ 11/11, 22/22, 35/35, 3/3, 8/8+2/2, 14/14+2/2, 35/35+2/2.
+
+## Traps for anyone touching this again
+
+- `--report-dir` is load-bearing. `run_python.py` only propagates failure when a
+ report was built; without it the gate runs to completion and always exits 0.
+- `run_notebook.py` writes executed outputs back **in place**. Correct for
+ generation, where the outputs are the product; wrong for a PR gate, which must
+ not dirty the tree it tests. Hence `--no-write-back`.
+- `JUPYTER_MISSING_RC` did **not** need promoting. It existed because the
+ workspace copies shelled out to a bare `jupyter`; the shared runner invokes
+ `sys.executable run_notebook.py`, so the abort-with-no-summary mode is
+ structurally absent. One planned promotion item dissolved on inspection.
+- A bare `off`/`on`/`yes` entry in `no_run.yaml` parses as a YAML **boolean** and
+ crashes `should_skip` with `TypeError: argument of type 'bool' is not
+ iterable`. Hit while building a fixture; not fixed, no repo currently has such
+ a script name.
+
+## Still open
+
+The merged `claude/smoke-copy-drift-ci-docs-ozntvv` branches across nine repos
+were not deleted — this session's git proxy refuses delete refspecs
+(`send-pack: unexpected disconnect`). They are all proven merged into `main`.
+
+## Original prompt
+
# run_smoke.py: three runner variants across 10 repos, no sync mechanism
Type: maintenance
diff --git a/complete/index.md b/complete/index.md
index 16f90c79..d07e2c98 100644
--- a/complete/index.md
+++ b/complete/index.md
@@ -6,7 +6,7 @@ Token-light navigation over the finished-work records (schema:
only then grep a dated bucket. Curators: edit the band between the CURATED
markers; everything below GENERATED is rebuilt.
-1093 records across 7 buckets.
+1094 records across 7 buckets.
## Highlights
@@ -194,6 +194,7 @@ _(curate hard-won records here — survives regeneration.)_
- [small-datasets-loader-pixel-scales](2026/08/small-datasets-loader-pixel-scales.md)
- [small-datasets-regime-stamp](2026/08/small-datasets-regime-stamp.md)
- [small-datasets-rmtree-committed-data](2026/08/small-datasets-rmtree-committed-data.md)
+- [smoke-runner-delegation](2026/08/smoke-runner-delegation.md)
- [smoke-runner-jupyter-guard](2026/08/smoke-runner-jupyter-guard.md)
- [spawn-autonomy-log-generated](2026/08/spawn-autonomy-log-generated.md)
- [spawn-drift-self-heal](2026/08/spawn-drift-self-heal.md)
diff --git a/dashboard.html b/dashboard.html
index 069bf8dc..1bf29b17 100644
--- a/dashboard.html
+++ b/dashboard.html
@@ -123,7 +123,7 @@
📋
PyAutoMindDashboard
Intent. Priority. Flow.
Every task the Mind is holding. Tap a task's 📋 and its /start_dev command is on your clipboard — paste it into a Claude Code chat to route Claude straight to that task. Recent is the same work by date — what has been happening rather than what to do next.