Three items that share one review and one merge, bundled deliberately: together they are the first end-to-end exercise of the canonical bump workflow in README.md (edit script → bump CANONICAL_SHA256 → merge → re-vendor per repo). That path has never been run start to finish. Doing it on a change that is also a real fix is worth more than doing it on a comment.
Filed from a session that started as a handoff read and turned into a verification pass. Every claim below was checked against the checkouts, not inferred.
1. templates/deps.yml is byte-identical to none of its callers
The template's own header says:
# STANDARDIZED ORG-WIDE CALLER — copy this file byte-identical to
# .github/workflows/deps.yml in the adopting repo.
It matches neither adopter. Both callers are pinned behind the template:
| file |
osv-scan pin |
templates/deps.yml |
62990dd15f1b0deba21e597bebd1512970544c15 |
infra/.github/workflows/infra-test.yml |
8b7d8a8d3fbf43a4a97b47006ae40dfe32012b85 |
front-desk-scheduler/.github/workflows/deps.yml |
8b7d8a8d3fbf43a4a97b47006ae40dfe32012b85 |
8b7d8a8 is an ancestor of 62990dd — the callers sit four commits back:
62990dd osv-scan: report-only silently did nothing — `bash -e` killed the triage
4e8e18f templates: pin to fa4620770395c57e4fc0d042ded33094b9e7d290
fa46207 osv-scan: add report-only adoption grace; ship it in the template (#2)
0a064f4 templates: the standardized org-wide caller (deps.yml)
So the callers predate the report-only input entirely (added in fa46207). Beyond the pin, the template also carries a weekly schedule (cron: "23 7 * * 2") and report-only: true; neither caller has either, and the comment blocks have diverged wholesale.
Nothing is broken today — neither caller passes report-only, so neither is affected by the triage bug 62990dd fixed. This is drift, not an outage. But it is drift the repo asserts does not exist.
Why it went unnoticed: only env-check has a drift gate. deps has none. This is precisely the condition #122 was filed to prevent, one lane over.
2. Ordering hazard for #2
This matters more than it looks, because #2 fans templates/deps.yml out byte-identical to 81 repos.
Executed as written today, the fleet lands on 62990dd + report-only: true while infra and front-desk-scheduler stay on 8b7d8a8 with no grace flag — turning a 2-repo inconsistency into an 83-repo one, with the two reference adopters as the outliers. #2's inventory also counts them as "3 covered", which overstates what covered means when neither file matches the artifact being rolled out.
The two callers should be re-pinned before wave 1, not after. Cheap now, 81 PRs of cleanup later.
Worth deciding alongside #8: the two existing callers are already hard-fail, so for them the report-only grace #8 wants removed is a step backwards if adopted verbatim.
3. self-test does not resolve template pins
Recorded as the fourth "hollow green" in infra/docs/cloud-sessions.md: self-test once passed on a commit whose reusable-workflow pin was a nonexistent SHA. Nothing in CI resolves pins, so @REPLACE_WITH_MERGE_SHA and a fabricated 40-hex string are equally invisible.
self-test covers the canonical script well (env-check, env-check-digest) and the converter well (check_embed_sync.py, goldens, bash -e triage). It does not check that the SHAs in templates/*.yml name real commits.
Add a step that resolves every pin in templates/*.yml with git cat-file -e <sha>^{commit}.
All three pins currently in the tree do resolve — verified on an unshallowed clone — so this gate lands green and stays green until it catches something. Note the shallow-clone trap for whoever writes it: under --depth 1 every cat-file fails, which would make the check fire constantly on a normal actions/checkout. It needs enough history to resolve, or a different resolution mechanism.
4. The canonical script does not say it is fleet-managed
tools/cloud-env-check.mjs describes adoption as:
// Repo-agnostic on purpose: everything repo-specific lives in the config file's
// "handshake" block, so another repo adopts this by copying two files and
// setting that block — no edits to this script.
env-check.yml already contradicts this in writing:
# Adoption is FOUR things, not two. The script's own header undercounts it:
Two problems. The header undercounts adoption (steps 3 and 4 — the SessionStart hook invocation and the dialog variable — live outside both files), and nothing in the vendored copy tells a reader that editing it locally is the wrong move. A vendored file with no provenance marker is an invitation to fix it in place, which is the exact failure the drift gate exists to catch — it would just catch it later and more expensively.
Add a header line naming bounded-systems/ci-workflows as canonical, stating that local edits belong upstream, and correcting two → four.
Open question, worth answering here rather than rediscovering
Should deps get the same drift gate env-check has?
Argument for: item 1 is exactly the failure the env-check gate was built to make loud, and it went unseen for a day across two repos. At 83 repos it will not stay a one-day problem.
Argument against: the two are not the same shape. env-check gates a script's bytes against a canonical copy. deps.yml is a caller whose only meaningful content is a pin — and its drift is one-directional and enumerable, so a resolver in self-test (item 3) plus a "does any caller lag the template" check may cover it without a per-repo lane. There is also a bootstrapping wrinkle: a gate on caller contents has to run in the caller's repo, which is 81 more adoption steps.
My read is that item 3 generalizes better and should be tried first — but this is a decision, and it should be made deliberately rather than by whoever next trips over it.
Checklist
Verification used
# current state, all three byte-identical and matching CANONICAL_SHA256
sha256sum tools/cloud-env-check.mjs # c530b86a…
diff templates/env-check.yml <caller>/.github/workflows/env-check.yml # identical, both repos
# the deps drift
diff templates/deps.yml front-desk-scheduler/.github/workflows/deps.yml
git merge-base --is-ancestor 8b7d8a8… 62990dd… # callers are behind
# pin resolution — requires an unshallowed clone
git fetch --unshallow
git cat-file -e <sha>^{commit}
Related: #2 (fleet rollout — the ordering hazard), #8 (report-only grace), infra#122 (the gate this pattern came from), infra#101 (tracking DAG).
Three items that share one review and one merge, bundled deliberately: together they are the first end-to-end exercise of the canonical bump workflow in
README.md(edit script → bumpCANONICAL_SHA256→ merge → re-vendor per repo). That path has never been run start to finish. Doing it on a change that is also a real fix is worth more than doing it on a comment.Filed from a session that started as a handoff read and turned into a verification pass. Every claim below was checked against the checkouts, not inferred.
1.
templates/deps.ymlis byte-identical to none of its callersThe template's own header says:
It matches neither adopter. Both callers are pinned behind the template:
templates/deps.yml62990dd15f1b0deba21e597bebd1512970544c15infra/.github/workflows/infra-test.yml8b7d8a8d3fbf43a4a97b47006ae40dfe32012b85front-desk-scheduler/.github/workflows/deps.yml8b7d8a8d3fbf43a4a97b47006ae40dfe32012b858b7d8a8is an ancestor of62990dd— the callers sit four commits back:So the callers predate the
report-onlyinput entirely (added infa46207). Beyond the pin, the template also carries a weeklyschedule(cron: "23 7 * * 2") andreport-only: true; neither caller has either, and the comment blocks have diverged wholesale.Nothing is broken today — neither caller passes
report-only, so neither is affected by the triage bug62990ddfixed. This is drift, not an outage. But it is drift the repo asserts does not exist.Why it went unnoticed: only
env-checkhas a drift gate.depshas none. This is precisely the condition #122 was filed to prevent, one lane over.2. Ordering hazard for #2
This matters more than it looks, because #2 fans
templates/deps.ymlout byte-identical to 81 repos.Executed as written today, the fleet lands on
62990dd+report-only: truewhileinfraandfront-desk-schedulerstay on8b7d8a8with no grace flag — turning a 2-repo inconsistency into an 83-repo one, with the two reference adopters as the outliers. #2's inventory also counts them as "3 covered", which overstates what covered means when neither file matches the artifact being rolled out.The two callers should be re-pinned before wave 1, not after. Cheap now, 81 PRs of cleanup later.
Worth deciding alongside #8: the two existing callers are already hard-fail, so for them the report-only grace #8 wants removed is a step backwards if adopted verbatim.
3.
self-testdoes not resolve template pinsRecorded as the fourth "hollow green" in
infra/docs/cloud-sessions.md:self-testonce passed on a commit whose reusable-workflow pin was a nonexistent SHA. Nothing in CI resolves pins, so@REPLACE_WITH_MERGE_SHAand a fabricated 40-hex string are equally invisible.self-testcovers the canonical script well (env-check,env-check-digest) and the converter well (check_embed_sync.py, goldens,bash -etriage). It does not check that the SHAs intemplates/*.ymlname real commits.Add a step that resolves every pin in
templates/*.ymlwithgit cat-file -e <sha>^{commit}.All three pins currently in the tree do resolve — verified on an unshallowed clone — so this gate lands green and stays green until it catches something. Note the shallow-clone trap for whoever writes it: under
--depth 1everycat-filefails, which would make the check fire constantly on a normalactions/checkout. It needs enough history to resolve, or a different resolution mechanism.4. The canonical script does not say it is fleet-managed
tools/cloud-env-check.mjsdescribes adoption as:env-check.ymlalready contradicts this in writing:Two problems. The header undercounts adoption (steps 3 and 4 — the SessionStart hook invocation and the dialog variable — live outside both files), and nothing in the vendored copy tells a reader that editing it locally is the wrong move. A vendored file with no provenance marker is an invitation to fix it in place, which is the exact failure the drift gate exists to catch — it would just catch it later and more expensively.
Add a header line naming
bounded-systems/ci-workflowsas canonical, stating that local edits belong upstream, and correcting two → four.Open question, worth answering here rather than rediscovering
Should
depsget the same drift gateenv-checkhas?Argument for: item 1 is exactly the failure the env-check gate was built to make loud, and it went unseen for a day across two repos. At 83 repos it will not stay a one-day problem.
Argument against: the two are not the same shape.
env-checkgates a script's bytes against a canonical copy.deps.ymlis a caller whose only meaningful content is a pin — and its drift is one-directional and enumerable, so a resolver inself-test(item 3) plus a "does any caller lag the template" check may cover it without a per-repo lane. There is also a bootstrapping wrinkle: a gate on caller contents has to run in the caller's repo, which is 81 more adoption steps.My read is that item 3 generalizes better and should be tried first — but this is a decision, and it should be made deliberately rather than by whoever next trips over it.
Checklist
infra/.github/workflows/infra-test.ymlandfront-desk-scheduler/.github/workflows/deps.ymlto the template's osv-scan SHA, or bump the template — decide which direction is canonicalscheduleandreport-onlydivergence between template and callers (see Remediation queue: clear findings and remove report-only grace (it has no expiry and no owner) #8)git cat-file -eresolver overtemplates/*.ymlpins toself-test, with enough checkout history to resolvetools/cloud-env-check.mjs; correct "two files" → four adoption stepsCANONICAL_SHA256inenv-check-drift.ymlin the same commit (sha256sum tools/cloud-env-check.mjs)infraandfront-desk-scheduler; bump both caller pins (README step 4 — the part that has never been exercised)Verification used
Related: #2 (fleet rollout — the ordering hazard), #8 (report-only grace), infra#122 (the gate this pattern came from), infra#101 (tracking DAG).