fix(harness): sync arm-loop.sh setup template to v7 + guard against drift - #223
Conversation
…rift arm-loop.sh lives twice: .claude/scripts/arm-loop.sh (what runs from the installed plugin cache) and .claude/skills/setup/templates/arm-loop.sh (what scaffold.sh writes into a consumer repo and sync.sh re-stamps from). Nothing kept them in sync — release.sh does not copy one onto the other and no check compared them — so they drifted: scripts/ was v7 while the template was still v6. Consequence downstream: sync.sh stamps consumers from the TEMPLATE, so every already-onboarded repo is reported "up to date" at v6 forever while the plugin's own runtime copy is v7. Observed in reDeploy, where /orchestrator:sync printed "up to date: .claude/scripts/arm-loop.sh already v6" against a v7 plugin. The skew is not cosmetic. v7 added --stop-after-days (issue #95), which writes .claude/state/loop-arming.json — the file loop-tick.sh reads to decide whether the loop has passed its self-disarm horizon. A consumer stamped at v6 arms a loop whose tick script (resolved from the plugin cache, so v7) expects state the v6 arming script never writes. Changes: - templates/arm-loop.sh := scripts/arm-loop.sh (now byte-identical, v7, executable bit preserved). - New .claude/scripts/managed-template-parity.test.sh: for every templates/ file with a same-named scripts/ twin, assert byte-identity and matching executable bit, so this cannot silently recur. Auto-discovered by checks.sh do_test() (.claude/scripts/*.test.sh) — no self/ changes needed. Verified it fails on the drifted tree and passes on the fixed one. No version bump here: release.sh owns plugin.json/marketplace.json versioning as part of a milestone-gated cut, so the fix reaches installed consumers on the next release. (CONTRIBUTING.md still says to bump plugin.json in the same PR as a managed-file change — that predates release.sh and now conflicts; flagged in the PR body rather than acted on unilaterally.) Gates: build + lint pass. test has 4 pre-existing failures in this sandbox (cockpit, loop-census, loop-tick, pr-feedback — all network/port-bound); verified byte-identical failure sets on pristine main and on this branch. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
fix(harness): sync arm-loop.sh setup template to v7 + guard against drift (not yet reviewed) |
|
CI on this PR was failing due to a GitHub Actions platform-wide outage (confirmed via githubstatus.com: "Partial System Outage", major incident starting 2026-08-06 17:02 UTC) — every failing job died at the "Getting action download info" setup step with |
|
fix(harness): sync arm-loop.sh setup template to v7 + guard against drift (not yet reviewed) |
What
arm-loop.shis maintained in two places:.claude/scripts/arm-loop.sh.claude/skills/setup/templates/arm-loop.shscaffold.shwrites into a consumer repo andsync.shre-stamps fromNothing kept them in sync —
release.shdoesn't copy one onto the other, and no check compared them. They drifted.Why it matters
sync.shstamps consumers from the template, so every already-onboarded repo is reported up to date at v6 forever while the plugin's own runtime copy is v7. Observed in reDeploy:This isn't cosmetic. v7 added
--stop-after-days(issue #95), which writes.claude/state/loop-arming.json— the fileloop-tick.shreads to decide whether the loop has passed its self-disarm horizon. A consumer stamped at v6 arms a loop whose tick script (resolved from the plugin cache, so v7) expects state the v6 arming script never writes.Changes
templates/arm-loop.sh:=scripts/arm-loop.sh— now byte-identical at v7, executable bit preserved..claude/scripts/managed-template-parity.test.sh— for everytemplates/file with a same-namedscripts/twin, asserts byte-identity and matching executable bit, so this can't silently recur. Auto-discovered bychecks.shdo_test()(.claude/scripts/*.test.sh), so noself/changes were needed (per CONTRIBUTING's "what NOT to touch").Verified the guard actually guards: it exits non-zero on the drifted tree and zero on the fixed one.
Gates
GATES_FILE=self/gates.json bash .claude/scripts/gate.sh <gate>:cockpit.test.sh,loop-census.test.sh,loop-tick.test.sh,pr-feedback.test.sh(all bind ports / need network). I ranself/checks.sh teston pristinemainand on this branch and got identical failure sets, so this PR neither causes nor fixes them. The new test passes.One question for you
No version bump in this PR.
release.sh(issue #176) ownsplugin.json/marketplace.jsonversioning as part of a milestone-gated cut, so this fix reaches installed consumers on the next release. ButCONTRIBUTING.md→ "What NOT to touch" still says a managed-file change must bumpplugin.json's version in the same PR. That guidance predatesrelease.shand now conflicts with it. I left the bump out rather than collide with the release driver — happy to add it, or to send a separate doc PR reconciling the two, whichever you prefer.Not in this PR (found alongside, both need a design call from you)
skills/setup/templates/settings.jsondeclaresPostToolUse(lint),Stop(test_affected)andPreToolUse(guard-git-add)— all three of whichhooks/hooks.jsonalso registers. A freshly-onboarded repo therefore runs each twice per turn; in reDeploy that meant two concurrentpnpm -r buildinto the samedist/, surfacing as a racyStop hook error: No stderr output. The#140hooks-parity check covers reCode's ownsettings.jsonvshooks.json, not the template, so it doesn't catch this. I did not touch it — the fix depends on whether the template's hooks are meant as a plugin-disabled fallback..claude/scripts/.templates/gates.yml:48andtemplates/action.yml:57runbash .claude/scripts/gate.sh, andtemplates/gates.json's worktree_notesays "Run via.claude/scripts/worktree.sh" — but Consumer repos carry stale vendored .claude/scripts that shadow the fresh plugin cache — manage them or stop vendoring #134 removed the vendoring that populated that directory, and CI runners have no plugin cache. In reDeploy this meantworktree.shwas never present at all: nothing ever copied it and no mechanism ever would.Happy to file both as
backlogissues if you'd rather track them that way.🤖 Generated with Claude Code