Overview
Heart's release-fidelity Stage 3 (workspace-validation.yml, mode=release) already runs verify_install checks A–E against the TestPyPI wheels and they pass — but the readiness verdict still reports install verification not run. The evidence is produced in CI and then thrown away: heart/validate.py reads the --verify-install sidecar only in the failure direction, and nothing ever writes the verify_install.json sidecar that readiness.py reads. This is one of three legs holding Heart at YELLOW 70. Because the armed nightly release driver's step-5 gate requires GREEN and has deliberately no force input, Heart never reaching GREEN means the nightly can never ship unattended — 2026.7.15.1 shipped only because a human forced it with release --force.
Verified on main at 2026-07-15 (measured, not inherited):
pyauto-heart readiness → YELLOW score 70, with ? install verification not run.
~/.pyauto-heart/verify_install.json — the file heart/state.py:84 reads — does not exist.
~/.pyauto-heart/validation_report.json from the real 2026-07-15 release run was ingested successfully (integrate stage pass, 543 passed / 0 failed) and contains zero trace of verify_install.
Discard point: heart/validate.py:563-569 — ready is False force-fails the stage; a PASS contributes nothing.
Plan
- Record
index (testpypi | pypi) in the verify_install.sh JSON sidecar so the result carries its own provenance.
- Embed the sidecar in the Stage 3 stage report, instead of only consulting it to force a failure.
- On
validate --ingest, persist that embedded result to HEART_STATE_DIR/verify_install.json — the file the readiness leg already reads.
- Surface the index in the readiness reason line, so a TestPyPI-wheel pass never reads as proof that a PyPI install works.
readiness.py needs no logic change: the leg starts receiving evidence through the path it already consumes.
Decided design question (human, 2026-07-15): a TestPyPI-wheel verify_install pass does satisfy this leg — the about-to-ship wheels are the right evidence for a release gate — but provenance is recorded so the verdict claims only what was verified. Precedent: the rehearse stage already records index: "testpypi".
Autonomy note (2026-07-15): launched with --auto, but work-type release caps at human-required (AUTONOMY.md:56), so effective level = human-required and --auto changed nothing. This is the cap working as intended: the change edits what makes Heart GREEN, which is the exact gate guarding the unattended nightly (AUTONOMY.md:85). Plan approved by the human; run ends at PR-open, merge stays human.
Detailed implementation plan
Affected Repositories
- PyAutoHeart (primary, only)
Branch Survey
| Repository |
Current Branch |
Dirty? |
| ./PyAutoHeart |
main |
clean |
Suggested branch: feature/wire-verify-install-leg (no worktree conflict)
Implementation Steps
-
heart/checks/verify_install.sh — in the --report-json sidecar writer, add "index": "testpypi" when USE_TESTPYPI=1, else "pypi". Pass it through the existing VI_* env-var handoff to the inline python3 writer (alongside VI_READY/VI_VERSION).
-
heart/validate.py → to_stage_report(...) — add a verify_install: dict | None param. When present, embed a normalized {ts, ready, version, index, checks} block under stage_report["verify_install"]. Keep force_fail/extra_failures behaviour exactly as-is (pure function, no I/O).
-
heart/validate.py CLI (--verify-install) — keep the force-fail path; additionally pass the parsed sidecar into to_stage_report so a pass survives into the artifact. Update the --verify-install help text, which currently documents only the fail direction.
-
heart/validate.py ingest (_Accumulator / run) — when an ingested stage report carries a verify_install block, persist it to state.HEART_STATE_DIR / "verify_install.json" via state.atomic_write_json. Idempotent on re-ingest; keep the newest ts. This is the one new write, and it stays inside ~/.pyauto-heart/ — the module boundary (no dispatch, no GitHub, no foreign repo) is preserved.
-
heart/readiness.py — no gate-logic change. Only extend the existing pass/stale reason strings to name the index (e.g. install verification stale (testpypi, 16d old)), so the provenance decision is visible in the verdict.
-
Tests — tests/test_validate.py: sidecar embeds into the stage report on pass; --ingest writes verify_install.json; re-ingest is idempotent; existing force-fail tests still green. tests/test_readiness.py: an ingested testpypi pass flips the leg off the stale tier; reason line carries the index. tests/test_verify_install_script.py: sidecar contains index.
-
Verification — after ingest of a stage report carrying a pass, pyauto-heart readiness must drop install verification not run, leaving the two known follow-up legs (stale test_run re-run; 58 stale parked no_run scripts). Those are explicitly out of scope per the prompt.
Key Files
heart/validate.py — to_stage_report (:395), CLI --verify-install (:541-570), ingest accumulator
heart/checks/verify_install.sh — sidecar writer (--report-json block)
heart/readiness.py:365-386 — the install-verification leg (consumer; near-untouched)
heart/state.py:84 — verify_install.json → snapshot key
.github/workflows/workspace-validation.yml — verify_install_release + emit_release_report (already wired; expected to need no change)
Original Prompt
Click to expand starting prompt
Wire Stage 3's verify_install result into Heart's install-verification readiness leg. Heart's release-fidelity Stage 3 (workspace-validation mode=release) already runs verify_install checks A-E against the TestPyPI wheels and they pass, but the readiness verdict still reports 'install verification not run' because that result is a separate ingest the leg never receives. Evidence is produced and then thrown away. This is one of three legs holding Heart at YELLOW 70; because the armed nightly release driver's step-5 gate requires GREEN and has deliberately no force input, Heart never reaching GREEN means the nightly can never ship unattended - 2026.7.15.1 shipped only because a human forced it with 'release --force'. Fixing this leg is the durable half: the other two are an ops re-run of the weekly mode=smoke workspace-validation to refresh the stale test_run leg (its 3 failures from 2026-07-09 are already disproven by the fresh release run: autolens_test 90p/0f, 543p/0f/0t overall), and hygiene triage of 58 stale parked no_run scripts (all age_days=90, category slow). Those two are follow-ups, not this task.
Overview
Heart's release-fidelity Stage 3 (
workspace-validation.yml,mode=release) already runsverify_installchecks A–E against the TestPyPI wheels and they pass — but the readiness verdict still reportsinstall verification not run. The evidence is produced in CI and then thrown away:heart/validate.pyreads the--verify-installsidecar only in the failure direction, and nothing ever writes theverify_install.jsonsidecar thatreadiness.pyreads. This is one of three legs holding Heart at YELLOW 70. Because the armed nightly release driver's step-5 gate requires GREEN and has deliberately no force input, Heart never reaching GREEN means the nightly can never ship unattended — 2026.7.15.1 shipped only because a human forced it withrelease --force.Verified on
mainat 2026-07-15 (measured, not inherited):pyauto-heart readiness→YELLOW score 70, with? install verification not run.~/.pyauto-heart/verify_install.json— the fileheart/state.py:84reads — does not exist.~/.pyauto-heart/validation_report.jsonfrom the real 2026-07-15 release run was ingested successfully (integrate stagepass, 543 passed / 0 failed) and contains zero trace of verify_install.Discard point:
heart/validate.py:563-569—ready is Falseforce-fails the stage; a PASS contributes nothing.Plan
index(testpypi|pypi) in theverify_install.shJSON sidecar so the result carries its own provenance.validate --ingest, persist that embedded result toHEART_STATE_DIR/verify_install.json— the file the readiness leg already reads.readiness.pyneeds no logic change: the leg starts receiving evidence through the path it already consumes.Decided design question (human, 2026-07-15): a TestPyPI-wheel
verify_installpass does satisfy this leg — the about-to-ship wheels are the right evidence for a release gate — but provenance is recorded so the verdict claims only what was verified. Precedent: therehearsestage already recordsindex: "testpypi".Autonomy note (2026-07-15): launched with
--auto, but work-typereleasecaps athuman-required(AUTONOMY.md:56), so effective level =human-requiredand--autochanged nothing. This is the cap working as intended: the change edits what makes Heart GREEN, which is the exact gate guarding the unattended nightly (AUTONOMY.md:85). Plan approved by the human; run ends at PR-open, merge stays human.Detailed implementation plan
Affected Repositories
Branch Survey
Suggested branch:
feature/wire-verify-install-leg(no worktree conflict)Implementation Steps
heart/checks/verify_install.sh— in the--report-jsonsidecar writer, add"index": "testpypi"whenUSE_TESTPYPI=1, else"pypi". Pass it through the existingVI_*env-var handoff to the inlinepython3writer (alongsideVI_READY/VI_VERSION).heart/validate.py→to_stage_report(...)— add averify_install: dict | Noneparam. When present, embed a normalized{ts, ready, version, index, checks}block understage_report["verify_install"]. Keepforce_fail/extra_failuresbehaviour exactly as-is (pure function, no I/O).heart/validate.pyCLI (--verify-install) — keep the force-fail path; additionally pass the parsed sidecar intoto_stage_reportso a pass survives into the artifact. Update the--verify-installhelp text, which currently documents only the fail direction.heart/validate.pyingest (_Accumulator/run) — when an ingested stage report carries averify_installblock, persist it tostate.HEART_STATE_DIR / "verify_install.json"viastate.atomic_write_json. Idempotent on re-ingest; keep the newestts. This is the one new write, and it stays inside~/.pyauto-heart/— the module boundary (no dispatch, no GitHub, no foreign repo) is preserved.heart/readiness.py— no gate-logic change. Only extend the existing pass/stale reason strings to name the index (e.g.install verification stale (testpypi, 16d old)), so the provenance decision is visible in the verdict.Tests —
tests/test_validate.py: sidecar embeds into the stage report on pass;--ingestwritesverify_install.json; re-ingest is idempotent; existing force-fail tests still green.tests/test_readiness.py: an ingested testpypi pass flips the leg off the stale tier; reason line carries the index.tests/test_verify_install_script.py: sidecar containsindex.Verification — after ingest of a stage report carrying a pass,
pyauto-heart readinessmust dropinstall verification not run, leaving the two known follow-up legs (staletest_runre-run; 58 stale parkedno_runscripts). Those are explicitly out of scope per the prompt.Key Files
heart/validate.py—to_stage_report(:395), CLI:541-570), ingest accumulator--verify-install(heart/checks/verify_install.sh— sidecar writer (--report-jsonblock)heart/readiness.py:365-386— the install-verification leg (consumer; near-untouched)heart/state.py:84—verify_install.json→ snapshot key.github/workflows/workspace-validation.yml—verify_install_release+emit_release_report(already wired; expected to need no change)Original Prompt
Click to expand starting prompt
Wire Stage 3's verify_install result into Heart's install-verification readiness leg. Heart's release-fidelity Stage 3 (workspace-validation mode=release) already runs verify_install checks A-E against the TestPyPI wheels and they pass, but the readiness verdict still reports 'install verification not run' because that result is a separate ingest the leg never receives. Evidence is produced and then thrown away. This is one of three legs holding Heart at YELLOW 70; because the armed nightly release driver's step-5 gate requires GREEN and has deliberately no force input, Heart never reaching GREEN means the nightly can never ship unattended - 2026.7.15.1 shipped only because a human forced it with 'release --force'. Fixing this leg is the durable half: the other two are an ops re-run of the weekly mode=smoke workspace-validation to refresh the stale test_run leg (its 3 failures from 2026-07-09 are already disproven by the fresh release run: autolens_test 90p/0f, 543p/0f/0t overall), and hygiene triage of 58 stale parked no_run scripts (all age_days=90, category slow). Those two are follow-ups, not this task.