.gitea/workflows/release.yml's G17 gate cloned sax3l/siax-templates on the moving branch main and then required that clone's HEAD to equal the static SIAX_TEMPLATES_PINNED_SHA. Any merge in siax-templates — including merges that touch nothing under ci/v2/reference-hooks — therefore fails this repo's release chain, with an error that says the gate is unreviewed when in fact nothing about the reviewed gate changed. Findings CD-03 (HIGH) and CI-RED-02.
It is red right now, measured today:
$ git clone --quiet --branch main https://git.siax.io/sax3l/siax-templates.git /tmp/x
$ git -C /tmp/x rev-parse HEAD
6612f6ae93c079806f64dc7c80eff746bac6a32c # main moved 2026-09-26 12:10Z
# pinned: 09504e3a593ee8ce0eaba606df31c349a3d52277 -> old logic exits 1
Root cause
The pin was enforced by comparing it against a branch tip instead of by checking it out. That makes the gate's pass/fail depend on unrelated activity in another repo. SIAX_TEMPLATES_REF: main was the load-bearing mistake, not the pin itself.
What this change does
The step now git init + git remote add + fetches the pinned commit by SHA (fetch --depth 1 origin $SIAX_TEMPLATES_PINNED_SHA) and checks it out detached. No branch is consulted.
Fallback: if the server refuses a by-SHA fetch (uploadpack.allowReachableSHA1InWant off), it fetches the branches and checks the same commit out of them. Either path lands the work tree on exactly the pinned commit, never on a branch tip.
Fails closed on the thing that actually matters: if the pinned commit cannot be checked out (removed or rewritten upstream), the step emits a ::error:: and exits 1.
The post-checkout actual_sha == pin assertion is kept, so the gate still proves on the run log which commit it executed.
SIAX_TEMPLATES_REF is removed from env: rather than left dangling, so the moving-branch shape cannot quietly return.
The pin value is unchanged.09504e3a593ee8ce0eaba606df31c349a3d52277 was verified to still exist upstream via the Gitea API before writing anything (GET /api/v1/repos/admin/siax-templates/git/commits/09504e3a… → 200, "Upload complete release evidence and validate artifact receipt (#138)", 2026-09-25T00:23:04Z; it is two commits behind main). Since the pinned commit exists, there was no reason to re-pin, and re-pinning would have undone the deliberate review in #119.
Two comments that asserted the old "refuses to run if main has moved past the pin" behaviour (.siax/hooks/G17.sh header, .siax/hooks/README.md "Pin status") are corrected in the same change, because this change makes them false.
How it was verified (commands actually run, on this branch's exact YAML)
The step's run: script was extracted from the committed YAML with yaml.safe_load and executed as-is, with one substitution: https://oauth2:${SIAX_CI_TOKEN}@git.siax.io/ → https://git.siax.io/, so auth came from the machine's git credential helper and no token was ever written to disk.
Not run: pnpm run build (next build) and the integration job — this diff touches no TypeScript, only a Gitea workflow and two comment blocks; the release chain itself cannot be executed outside CI (it needs SIAX_CI_TOKEN, registry, cosign and AUD0 secrets, none of which are provisioned on this repo — see .siax/hooks/README.md).
Risk
Low, and confined to the gate's own fetch step.
The supply-chain property is preserved, not weakened: CI still runs exactly one specific reviewed commit of siax-templates, asserted after checkout. The property that is removed is "fail whenever someone else merges", which was noise, not security.
The checkout is shallow on the by-SHA path. Verified safe: nothing under ci/v2/reference-hooks/ uses git history (grep for git rev-parse|git log|git describe|SIAX_TEMPLATES_DIR → no hits).
Same authenticated-clone-at-runtime mechanism and same SIAX_CI_TOKEN as before; no new secrets, no new permissions, no deploy behaviour touched.
New failure mode if someone force-pushes away the pinned commit in siax-templates: the gate fails with an explicit message telling the reviewer to re-review and bump the pin. That is the intended fail-closed direction.
Rollback
git revert 65ee88a — single commit, three files, no state anywhere else. The gate returns to comparing main (and to being red). Nothing is deployed, tagged, or published by this PR.
Out of scope, noticed while working here
Not touched, because they belong to the pin-bump work item, not this one:
.siax/hooks/README.md "Pin status" still names 384e35060b91… and .siax/hook-pins.jsontemplateRef still names a401880aa19a…; both predate #119's bump of the workflow to 09504e3a…. release.yml is authoritative and is the value CI uses — this PR's README edit says so explicitly.
The same moving-branch pattern exists in 0pt-motor, act0 and h0st; those are being fixed in their own PRs.
## Problem
`.gitea/workflows/release.yml`'s G17 gate cloned `sax3l/siax-templates` on the **moving** branch `main` and then required that clone's HEAD to equal the static `SIAX_TEMPLATES_PINNED_SHA`. Any merge in `siax-templates` — including merges that touch nothing under `ci/v2/reference-hooks` — therefore fails this repo's release chain, with an error that says the gate is unreviewed when in fact nothing about the reviewed gate changed. Findings **CD-03 (HIGH)** and **CI-RED-02**.
It is red right now, measured today:
```
$ git clone --quiet --branch main https://git.siax.io/sax3l/siax-templates.git /tmp/x
$ git -C /tmp/x rev-parse HEAD
6612f6ae93c079806f64dc7c80eff746bac6a32c # main moved 2026-09-26 12:10Z
# pinned: 09504e3a593ee8ce0eaba606df31c349a3d52277 -> old logic exits 1
```
## Root cause
The pin was enforced by *comparing* it against a branch tip instead of by *checking it out*. That makes the gate's pass/fail depend on unrelated activity in another repo. `SIAX_TEMPLATES_REF: main` was the load-bearing mistake, not the pin itself.
## What this change does
- The step now `git init` + `git remote add` + **fetches the pinned commit by SHA** (`fetch --depth 1 origin $SIAX_TEMPLATES_PINNED_SHA`) and checks it out detached. No branch is consulted.
- Fallback: if the server refuses a by-SHA fetch (`uploadpack.allowReachableSHA1InWant` off), it fetches the branches and checks the same commit out of them. Either path lands the work tree on exactly the pinned commit, never on a branch tip.
- Fails closed on the thing that actually matters: if the pinned commit cannot be checked out (removed or rewritten upstream), the step emits a `::error::` and exits 1.
- The post-checkout `actual_sha == pin` assertion is **kept**, so the gate still proves on the run log which commit it executed.
- `SIAX_TEMPLATES_REF` is removed from `env:` rather than left dangling, so the moving-branch shape cannot quietly return.
- **The pin value is unchanged.** `09504e3a593ee8ce0eaba606df31c349a3d52277` was verified to still exist upstream via the Gitea API before writing anything (`GET /api/v1/repos/admin/siax-templates/git/commits/09504e3a…` → 200, "Upload complete release evidence and validate artifact receipt (#138)", 2026-09-25T00:23:04Z; it is two commits behind `main`). Since the pinned commit exists, there was no reason to re-pin, and re-pinning would have undone the deliberate review in #119.
- Two comments that asserted the old "refuses to run if `main` has moved past the pin" behaviour (`.siax/hooks/G17.sh` header, `.siax/hooks/README.md` "Pin status") are corrected in the same change, because this change makes them false.
## How it was verified (commands actually run, on this branch's exact YAML)
The step's `run:` script was extracted from the committed YAML with `yaml.safe_load` and executed as-is, with one substitution: `https://oauth2:${SIAX_CI_TOKEN}@git.siax.io/` → `https://git.siax.io/`, so auth came from the machine's git credential helper and no token was ever written to disk.
| # | What | Result |
|---|------|--------|
| 1 | `python3 -c "yaml.safe_load(open('.gitea/workflows/release.yml'))"` | parses; `env` no longer contains `SIAX_TEMPLATES_REF` |
| 2 | `bash -n` + `shellcheck -s bash` on the extracted step | both clean, exit 0, no exclusions |
| 3 | Happy path: step run with pin `09504e3a…` against live `siax-templates` | exit 0 — `siax-templates G17 reference @ 09504e3a593ee8ce0eaba606df31c349a3d52277 (pinned commit, checked out directly)`; `rev-parse HEAD` = the pin; `ci/v2/reference-hooks/G17.sh` present and `chmod +x` succeeded on all 4 hooks + 2 `lib/*.sh` |
| 4 | Fallback path forced (plain `git fetch origin`, then checkout the pin) | exit 0, HEAD = `09504e3a…` — pin is reachable from a branch, so the fallback lands on the same commit |
| 5 | Fail-closed: pin set to `deadbeef…` (commit does not exist) | by-SHA fetch refused → fallback fetch → `checkout` fails → `::error::siax-templates commit deadbeef… not found …`, **exit 1** |
| 6 | Old logic on the same inputs, for contrast | `clone --branch main` → HEAD `6612f6ae…` ≠ pin → would exit 1 (the bug) |
| 7 | `sh scripts/check-masterplan-lock.sh` (runs on every branch push) | `masterplan lock check OK` |
| 8 | `pnpm install --frozen-lockfile` → `pnpm run typecheck` | install exit 0; `tsc -p tsconfig.check.json` exit 0 |
| 9 | `pnpm run test` (vitest, CI-blocking) | **95 files passed, 22 skipped; 1117 tests passed, 184 skipped**, exit 0 |
Not run: `pnpm run build` (next build) and the integration job — this diff touches no TypeScript, only a Gitea workflow and two comment blocks; the release chain itself cannot be executed outside CI (it needs `SIAX_CI_TOKEN`, registry, cosign and AUD0 secrets, none of which are provisioned on this repo — see `.siax/hooks/README.md`).
## Risk
Low, and confined to the gate's own fetch step.
- The supply-chain property is preserved, not weakened: CI still runs exactly one specific reviewed commit of `siax-templates`, asserted after checkout. The property that is *removed* is "fail whenever someone else merges", which was noise, not security.
- The checkout is shallow on the by-SHA path. Verified safe: nothing under `ci/v2/reference-hooks/` uses git history (`grep` for `git rev-parse|git log|git describe|SIAX_TEMPLATES_DIR` → no hits).
- Same authenticated-clone-at-runtime mechanism and same `SIAX_CI_TOKEN` as before; no new secrets, no new permissions, no deploy behaviour touched.
- New failure mode if someone force-pushes away the pinned commit in `siax-templates`: the gate fails with an explicit message telling the reviewer to re-review and bump the pin. That is the intended fail-closed direction.
## Rollback
`git revert 65ee88a` — single commit, three files, no state anywhere else. The gate returns to comparing `main` (and to being red). Nothing is deployed, tagged, or published by this PR.
## Out of scope, noticed while working here
Not touched, because they belong to the pin-bump work item, not this one:
- `.siax/hooks/README.md` "Pin status" still names `384e35060b91…` and `.siax/hook-pins.json` `templateRef` still names `a401880aa19a…`; both predate #119's bump of the workflow to `09504e3a…`. `release.yml` is authoritative and is the value CI uses — this PR's README edit says so explicitly.
- The same moving-branch pattern exists in `0pt-motor`, `act0` and `h0st`; those are being fixed in their own PRs.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
The G17 gate cloned sax3l/siax-templates on the moving branch `main` and
then required that clone's HEAD to equal SIAX_TEMPLATES_PINNED_SHA. Every
merge in siax-templates -- including ones that touch nothing under
ci/v2/reference-hooks -- therefore turned this repo's release chain red
while nothing was actually unsafe (CD-03 / CI-RED-02). It is red right now:
siax-templates main is at 6612f6ae, the pin is 09504e3a.
The pin is still a real supply-chain control; what was wrong was tying it
to someone else's branch tip. The step now fetches the pinned commit by
SHA (shallow, no branch consulted) and checks it out detached, falling back
to fetching the branches and taking the same commit out of them if the
server refuses a by-SHA fetch. It fails closed on the thing that actually
matters -- the reviewed commit no longer existing upstream -- and the
post-checkout HEAD assertion is kept so the gate still proves what it ran.
SIAX_TEMPLATES_REF is removed rather than left unused, so the moving-branch
shape cannot quietly come back. The pin value itself is unchanged: commit
09504e3a is confirmed present in siax-templates (Gitea API, two commits
behind main). Two comments that described the old "refuses to run if main
moved" behaviour are corrected in the same change.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
CODEOWNERS rules
requested review from sax3l 2026-09-26 18:08:39 +00:00
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Problem
.gitea/workflows/release.yml's G17 gate clonedsax3l/siax-templateson the moving branchmainand then required that clone's HEAD to equal the staticSIAX_TEMPLATES_PINNED_SHA. Any merge insiax-templates— including merges that touch nothing underci/v2/reference-hooks— therefore fails this repo's release chain, with an error that says the gate is unreviewed when in fact nothing about the reviewed gate changed. Findings CD-03 (HIGH) and CI-RED-02.It is red right now, measured today:
Root cause
The pin was enforced by comparing it against a branch tip instead of by checking it out. That makes the gate's pass/fail depend on unrelated activity in another repo.
SIAX_TEMPLATES_REF: mainwas the load-bearing mistake, not the pin itself.What this change does
git init+git remote add+ fetches the pinned commit by SHA (fetch --depth 1 origin $SIAX_TEMPLATES_PINNED_SHA) and checks it out detached. No branch is consulted.uploadpack.allowReachableSHA1InWantoff), it fetches the branches and checks the same commit out of them. Either path lands the work tree on exactly the pinned commit, never on a branch tip.::error::and exits 1.actual_sha == pinassertion is kept, so the gate still proves on the run log which commit it executed.SIAX_TEMPLATES_REFis removed fromenv:rather than left dangling, so the moving-branch shape cannot quietly return.09504e3a593ee8ce0eaba606df31c349a3d52277was verified to still exist upstream via the Gitea API before writing anything (GET /api/v1/repos/admin/siax-templates/git/commits/09504e3a…→ 200, "Upload complete release evidence and validate artifact receipt (#138)", 2026-09-25T00:23:04Z; it is two commits behindmain). Since the pinned commit exists, there was no reason to re-pin, and re-pinning would have undone the deliberate review in #119.mainhas moved past the pin" behaviour (.siax/hooks/G17.shheader,.siax/hooks/README.md"Pin status") are corrected in the same change, because this change makes them false.How it was verified (commands actually run, on this branch's exact YAML)
The step's
run:script was extracted from the committed YAML withyaml.safe_loadand executed as-is, with one substitution:https://oauth2:${SIAX_CI_TOKEN}@git.siax.io/→https://git.siax.io/, so auth came from the machine's git credential helper and no token was ever written to disk.python3 -c "yaml.safe_load(open('.gitea/workflows/release.yml'))"envno longer containsSIAX_TEMPLATES_REFbash -n+shellcheck -s bashon the extracted step09504e3a…against livesiax-templatessiax-templates G17 reference @ 09504e3a593ee8ce0eaba606df31c349a3d52277 (pinned commit, checked out directly);rev-parse HEAD= the pin;ci/v2/reference-hooks/G17.shpresent andchmod +xsucceeded on all 4 hooks + 2lib/*.shgit fetch origin, then checkout the pin)09504e3a…— pin is reachable from a branch, so the fallback lands on the same commitdeadbeef…(commit does not exist)checkoutfails →::error::siax-templates commit deadbeef… not found …, exit 1clone --branch main→ HEAD6612f6ae…≠ pin → would exit 1 (the bug)sh scripts/check-masterplan-lock.sh(runs on every branch push)masterplan lock check OKpnpm install --frozen-lockfile→pnpm run typechecktsc -p tsconfig.check.jsonexit 0pnpm run test(vitest, CI-blocking)Not run:
pnpm run build(next build) and the integration job — this diff touches no TypeScript, only a Gitea workflow and two comment blocks; the release chain itself cannot be executed outside CI (it needsSIAX_CI_TOKEN, registry, cosign and AUD0 secrets, none of which are provisioned on this repo — see.siax/hooks/README.md).Risk
Low, and confined to the gate's own fetch step.
siax-templates, asserted after checkout. The property that is removed is "fail whenever someone else merges", which was noise, not security.ci/v2/reference-hooks/uses git history (grepforgit rev-parse|git log|git describe|SIAX_TEMPLATES_DIR→ no hits).SIAX_CI_TOKENas before; no new secrets, no new permissions, no deploy behaviour touched.siax-templates: the gate fails with an explicit message telling the reviewer to re-review and bump the pin. That is the intended fail-closed direction.Rollback
git revert 65ee88a— single commit, three files, no state anywhere else. The gate returns to comparingmain(and to being red). Nothing is deployed, tagged, or published by this PR.Out of scope, noticed while working here
Not touched, because they belong to the pin-bump work item, not this one:
.siax/hooks/README.md"Pin status" still names384e35060b91…and.siax/hook-pins.jsontemplateRefstill namesa401880aa19a…; both predate #119's bump of the workflow to09504e3a….release.ymlis authoritative and is the value CI uses — this PR's README edit says so explicitly.0pt-motor,act0andh0st; those are being fixed in their own PRs.🤖 Generated with Claude Code