Overview
Spawn Drift is a drift check with no generator behind it. The templates are declared a generated view, but the only thing that ever regenerates them is a human typing /spawn --apply. So the scheduled leg goes red, stays red, and is cleared by hand.
Both legs run identical code — the workflow has no trigger-dependent behaviour beyond the PR-only if: added in #118. The difference is when each runs relative to a human --apply:
| date |
template force-synced |
dispatch run |
gap |
result |
| 2026-07-13 |
11:08:46Z |
11:09:02Z |
16 s |
PASS |
| 2026-07-27 |
19:30:29Z |
19:30:48Z |
19 s |
PASS |
A leg fired seconds after the human stamped the templates cannot fail. Every green run in the workflow's history is one of those.
Plan
Mirror the lifecycle_drift.yml self-heal (#116), which solved the same shape of problem — an alarm-only check just emails a human once per push.
On schedule: regenerate and, if the tree differs, open a PR on the affected template repo rather than pushing. Do NOT copy lifecycle_drift's direct bot-push: these are force-synced generated views, so an automated push is a force-push to a published repo's main. A PR keeps the sanctioned-force-push exception a human act while removing the "nothing regenerates" gap — and #118 is the argument for a human seeing what is about to be published.
Detailed implementation plan
Affected Repositories
Branch Survey
| Repository |
Current Branch |
Dirty? |
| ./PyAutoMind |
main |
clean |
Suggested branch: feature/spawn-drift-self-heal
Implementation Steps
scripts/spawn.py — distinguish the two failure classes, which --check currently collapses into exit 1:
- 0 clean
- 1 content drift only → auto-fixable, safe to propose
- 2 unsafe generation (UNMATCHED or canary hits) → a human decision, must NEVER be auto-PRed
The distinction is the safety interlock: a canary hit means the generated tree contains instance content, so opening a PR would propose publishing a leak.
.github/workflows/spawn_drift.yml — on schedule/workflow_dispatch, branch on that exit code. On 1: regenerate, push a stable spawn/auto-sync branch to each drifted template repo, open or update a PR. On 2: fail, no PR.
- Idempotence: a fixed branch name force-updated each run, so a weekly rerun refreshes the existing PR rather than opening a new one.
- Tests for the exit-code contract.
Key Files
scripts/spawn.py — main() exit path
.github/workflows/spawn_drift.yml
tests/test_spawn_template_contract.py
Known external dependency — flagging explicitly
Opening a PR on PyAutoMind-template / PyAutoMemory-template needs a token with write access to those repos; secrets.GITHUB_TOKEN is scoped to PyAutoMind only. The org's established cross-repo token is secrets.PAT_PYAUTOLABS (used by nightly-release.yml and PyAutoHands/release.yml).
Whether that PAT actually grants write to the two template repos cannot be verified from a local session — only a real run reveals it. The workflow therefore fails with an explicit, actionable message rather than silently doing nothing if the push or PR is refused.
Original Prompt
Click to expand starting prompt
draft/maintenance/pyautomind/spawn_drift_has_no_generator.md — filed during the #118 investigation.
Overview
Spawn Driftis a drift check with no generator behind it. The templates are declared a generated view, but the only thing that ever regenerates them is a human typing/spawn --apply. So the scheduled leg goes red, stays red, and is cleared by hand.Both legs run identical code — the workflow has no trigger-dependent behaviour beyond the PR-only
if:added in #118. The difference is when each runs relative to a human--apply:11:08:46Z11:09:02Z19:30:29Z19:30:48ZA leg fired seconds after the human stamped the templates cannot fail. Every green run in the workflow's history is one of those.
Plan
Mirror the
lifecycle_drift.ymlself-heal (#116), which solved the same shape of problem — an alarm-only check just emails a human once per push.On schedule: regenerate and, if the tree differs, open a PR on the affected template repo rather than pushing. Do NOT copy lifecycle_drift's direct bot-push: these are force-synced generated views, so an automated push is a force-push to a published repo's
main. A PR keeps the sanctioned-force-push exception a human act while removing the "nothing regenerates" gap — and #118 is the argument for a human seeing what is about to be published.Detailed implementation plan
Affected Repositories
Branch Survey
Suggested branch:
feature/spawn-drift-self-healImplementation Steps
scripts/spawn.py— distinguish the two failure classes, which--checkcurrently collapses into exit 1:The distinction is the safety interlock: a canary hit means the generated tree contains instance content, so opening a PR would propose publishing a leak.
.github/workflows/spawn_drift.yml— onschedule/workflow_dispatch, branch on that exit code. On 1: regenerate, push a stablespawn/auto-syncbranch to each drifted template repo, open or update a PR. On 2: fail, no PR.Key Files
scripts/spawn.py—main()exit path.github/workflows/spawn_drift.ymltests/test_spawn_template_contract.pyKnown external dependency — flagging explicitly
Opening a PR on
PyAutoMind-template/PyAutoMemory-templateneeds a token with write access to those repos;secrets.GITHUB_TOKENis scoped to PyAutoMind only. The org's established cross-repo token issecrets.PAT_PYAUTOLABS(used bynightly-release.ymlandPyAutoHands/release.yml).Whether that PAT actually grants write to the two template repos cannot be verified from a local session — only a real run reveals it. The workflow therefore fails with an explicit, actionable message rather than silently doing nothing if the push or PR is refused.
Original Prompt
Click to expand starting prompt
draft/maintenance/pyautomind/spawn_drift_has_no_generator.md— filed during the #118 investigation.