Repository navigation
[Phase 1] Drift-guard CI: fail when template stubs diverge from standards/ #969
Copy link
Copy link
Closed
Labels
dev-leadFor dev-lead agent pickupFor dev-lead agent pickupinitiativeEpic / initiative tracking issueEpic / initiative tracking issue
Description
Story
As a standards maintainer,
I want have a CI check that fails if repo-template's stubs/config diverge from the canonical standards/,
so that the template stays a thin distribution layer and never becomes a competing fork of the standards.
Acceptance Criteria
- A CI check compares each templated stub's git blob SHA against the canonical standards/workflows/ SHA and fails on any DRIFTED stub, reusing the ALIGNED/DRIFTED/MISSING classification model from scripts/fleet_stub_drift.sh.
- The check covers the workflow stubs plus the verbatim baseline (dependabot.yml, CODEOWNERS, CLAUDE.md); intentionally per-stack-customizable files (ci.yml) are excluded via a documented allowlist with a recorded rationale.
- The check is wired into an existing PR-triggered job (no new scheduled/cron workload) and emits a clear annotation naming the drifted file and both SHAs.
- A bats test exercises the drift classification (ALIGNED / DRIFTED / MISSING) for the template's stub set.
Tasks / Subtasks
- Reuse/extend scripts/fleet_stub_drift.sh's classify_stub_drift / stub_drift_row pure functions for the template's stub set (blob-SHA equality vs the canonical standards/workflows/ SHAs). (AC: test issue from agent #1)
- Define a documented allowlist excluding ci.yml (and any other intentionally-customized files) from byte-identity drift, with the rationale recorded alongside the AGENTS.md exception list. (AC: Go-live improvements for PR review agent #2)
- Wire the check into a PR-triggered lint job (no cron added). (AC: Add @claude delegation, auto-merge, and rebase handling #3)
- Add a bats test mirroring tests/fleet_stub_drift.bats for the template stub set. (AC: Optimize review: small-PR and incremental fast paths #4)
Dev Notes
- scripts/fleet_stub_drift.sh ALREADY implements exactly this byte-identity drift model (git blob SHA equality) for the initiative-planner.yml stub across fleet repos: classify_stub_drift <canonical_sha> <repo_sha> -> ALIGNED|DRIFTED|MISSING; stub_drift_row emits
repo<TAB>status<TAB>repo_sha<TAB>canonical_sha. Its functions are PURE (no network) — the network fetch lives in fleet_monitor.sh. Reuse them; do not reinvent a diff. - ci.yml is the ONE stub carrying real per-stack build/test steps and MUST be excluded from byte-identity drift (it is intentionally customized per consumer). Document the exclusion the same way AGENTS.md documents its lint.yml / token-report.yml / pr-review-sweep.yml exceptions.
- Honor the initiative cost cap: attach to PR triggers within the existing lint surface — add NO new cron/scheduled workload.
- Per the discussion's adversarial-review fallback: if a committed-stub drift guard proves too costly, the alternative is generating the template contents from standards/ in CI; keep that option open but the primary design is the drift check.
- Testing standard: bats in tests/, listed in the lint.yml bats job; mirror tests/fleet_stub_drift.bats.
Project Structure Notes
The drift check may live in repo-template's CI, in petry-projects/.github, or here; the reusable comparison logic and its bats test belong in this repo's scripts/ + tests/ to leverage the existing fleet_stub_drift.sh primitives.
References
- scripts/fleet_stub_drift.sh
- tests/fleet_stub_drift.bats
- .github/workflows/lint.yml
- AGENTS.md
- discussion Create a GitHub template repository to scaffold new petry-projects repos with org standards #962
Likely target surface
scripts/fleet_stub_drift.shtests/fleet_stub_drift.bats.github/workflows/lint.yml
Story prepared by the BMAD Scrum Master (Bob) for epic #964. Status: ready-for-dev.
Reactions are currently unavailable
Metadata
Metadata
Assignees
Labels
dev-leadFor dev-lead agent pickupFor dev-lead agent pickupinitiativeEpic / initiative tracking issueEpic / initiative tracking issue
Activity
don-petry commentedon Jun 28, 2026
Dev-Lead Implementation Plan
Issue: #969 — [Phase 1] Drift-guard CI: fail when template stubs diverge from standards/
Scope
Add a PR-triggered CI guard that fails when any repo-template stub/baseline file
drifts from its standards-derived baseline, reusing
fleet_stub_drift.sh'sbyte-identity (blob-SHA equality) ALIGNED/DRIFTED/MISSING model and excluding the
intentionally per-stack-customized
ci.ymlvia a documented allowlist.Design
The repo-template scaffold is a distribution artifact of
standards/produced byseed-repo-template.sh(callers repinned to@<name>/stable, inline stubs/baselineshipped from standards). The guard compares, per covered file:
git hash-objectofseed-repo-template.sh --emit-*output(the standards-derived content, repin included), and
.shaof the file inpetry-projects/repo-template.Equal SHAs ⇒ the template still tracks standards. The pure classification reuses
classify_stub_drift/stub_drift_row; network (gh + git hash-object) lives onlyin the CLI driver, mirroring
fleet_stub_drift.sh↔fleet_monitor.sh.Implementation Checklist
scripts/template_stub_drift.sh— sourcesfleet_stub_drift.sh; covered-filemanifest (9 workflow stubs +
dependabot.yml/CODEOWNERS/CLAUDE.md); documentedci.ymlallowlist;template_drift_allowlisted,template_drift_covered,template_drift_annotate(emits::error::naming file + both SHAs, fails on DRIFTED);network CLI driver
main(source-guarded)..github/workflows/lint.yml— new PR-triggeredtemplate-driftjob running thescript (no cron added); add
tests/template_stub_drift.batsto thebatsjob list.AGENTS.md— record theci.ymlallowlist exclusion + rationale alongside theexisting exception list.
Tests to Write
tests/template_stub_drift.bats(mirrorstests/fleet_stub_drift.bats):classification ALIGNED/DRIFTED/MISSING over the template's covered set;
ci.ymlis allowlisted/excluded fromtemplate_drift_covered;template_drift_annotateemits a::error::annotation naming the drifted fileand both SHAs and returns non-zero on DRIFTED, zero on a clean set.
Test Command
scripts/run-bats.sh tests/template_stub_drift.bats(full suite vialint.ymlbats job);lint via
bash .dev-lead/scripts/dev-lead-lint.sh+shellcheck scripts/*.sh.don-petry commentedon Jun 28, 2026
Dev-Lead: Implementation Complete
Plan Execution
scripts/template_stub_drift.sh— sourcesfleet_stub_drift.shand reusesclassify_stub_drift/stub_drift_rowfor the template's stub set (AC test issue from agent #1)TEMPLATE_DRIFT_ALLOWLIST) excludingci.yml, with rationale recorded alongside theAGENTS.mdexception list (AC Go-live improvements for PR review agent #2)lint.ymlas a newtemplate-driftjob — no cron/scheduled workload added (AC Add @claude delegation, auto-merge, and rebase handling #3)tests/template_stub_drift.batsmirroringtests/fleet_stub_drift.bats, added to thelint.ymlbats list (AC Optimize review: small-PR and incremental fast paths #4)Design
repo-templateis a distribution artifact ofstandards/produced byseed-repo-template.sh(caller stubs repinned to@<name>/stable, inline stubs + baseline shipped from standards). The guard compares, per covered file:git hash-objectofseed-repo-template.sh --emit-*output (the standards-derived content, repin included), vs.shaof the file inpetry-projects/repo-template.Equal blob SHAs ⇒ the template still tracks
standards/. Pure classification reusesfleet_stub_drift.sh; network (gh+git hash-object) lives only in the CLI drivermain, mirroringfleet_stub_drift.sh↔fleet_monitor.sh. On any DRIFTED file it emits a::error file=…::annotation naming the file and both SHAs and exits non-zero.Covered set: 9 workflow stubs (agent-shield, auto-rebase, copilot-setup-steps, dependabot-automerge, dependabot-rebase, dependency-audit, dev-lead, pr-review-mention, sonarcloud) + verbatim baseline (
.github/dependabot.yml,.github/CODEOWNERS,CLAUDE.md). Excluded:.github/workflows/ci.yml(per-stack customizable).Test Results
Files Changed
scripts/template_stub_drift.sh(new): pure drift helpers (allowlist, covered set, annotate) reusingfleet_stub_drift.sh+ a source-guarded network CLI driver.tests/template_stub_drift.bats(new): ALIGNED/DRIFTED/MISSING classification, allowlist, and annotation tests..github/workflows/lint.yml: newtemplate-driftPR job +template_stub_drift.batsadded to the bats list.AGENTS.md: new "Template drift guard (repo-template)" section documenting theci.ymlallowlist exception + rationale.Notes
::warning::(matches the informational treatment infleet_stub_drift.sh).repo-templatenot being readable bygithub.tokenif private) becomes a::warning::skip rather than a hard failure, so the PR lint job never flakes on infrastructure — only real drift fails it. The drift check could alternatively live inrepo-template's own CI (where it has native read access); the reusable logic + bats test live here per the story's structure note.dependabot.ymldrift assumes the template ships the defaultnodestack (DEPENDABOT_STACK); a different stack would need that env set.feat: implement issue #969 — [Phase 1] Drift-guard CI: fail when temp…
feat: implement issue #969 — [Phase 1] Drift-guard CI: fail when temp…
feat: implement issue #969 — [Phase 1] Drift-guard CI: fail when temp…
feat: implement issue #969 — [Phase 1] Drift-guard CI: fail when temp…
feat: implement issue #969 — [Phase 1] Drift-guard CI: fail when temp…
feat: implement issue #969 — [Phase 1] Drift-guard CI: fail when temp…
feat: implement issue #969 — [Phase 1] Drift-guard CI: fail when temp…
feat: implement issue #969 — [Phase 1] Drift-guard CI: fail when temp…
feat: implement issue #969 — [Phase 1] Drift-guard CI: fail when temp…
feat: implement issue #969 — [Phase 1] Drift-guard CI: fail when temp…
feat: implement issue #969 — [Phase 1] Drift-guard CI: fail when temp…
feat: implement issue #969 — [Phase 1] Drift-guard CI: fail when temp…
chore: sync template stubs with standards baseline
feat: implement issue #1647 — [qa-lead S4] Make persona.yml's declare…