From 21f0aa8c7b3884a2d809ecfc1699940e5d718e03 Mon Sep 17 00:00:00 2001 From: "Kirill D." Date: Sun, 28 Jun 2026 14:46:25 -0600 Subject: [PATCH 1/2] docs: pin consumer Proof Gate template to the Runcap v0.6.0 release SHA Replace the all-zero placeholder in examples/runcap-adjudicate.yml with the immutable v0.6.0 release commit (1eb8745). Reword the README and template comments from "replace the all-zero placeholder" to "pinned to Runcap v0.6.0; when intentionally upgrading, replace the SHA with the exact release commit", and keep the explicit rule: never use @v1 or a floating tag for the Proof Gate. Co-Authored-By: Claude Opus 4.7 --- README.md | 4 ++-- examples/runcap-adjudicate.yml | 15 +++++++++------ 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 16e2683..bc496f2 100644 --- a/README.md +++ b/README.md @@ -337,11 +337,11 @@ Make the adjudication a required red/green PR check in your own repo: 1. Add `.runcap/mission.yaml` (the policy - see the example above). 2. Copy `examples/runcap-adjudicate.yml` into `.github/workflows/`. -3. Replace the all-zero `RUNCAP_ACTION_SHA` placeholder with the full immutable commit SHA of the released version (resolve it with `gh api repos/kirder24-code/ai-agent-manager/git/refs/tags/vX.Y.Z --jq '.object.sha'`). +3. The template is pinned to Runcap v0.6.0. When intentionally upgrading, replace the SHA with the exact commit SHA of the release you choose (resolve it with `gh api repos/kirder24-code/ai-agent-manager/git/refs/tags/vX.Y.Z --jq '.object.sha'`). Never use `@v1` or another floating tag for the Proof Gate. Use a full immutable commit SHA. 4. Configure the hardened GitHub branch profile (protected branch, required check, up-to-date-before-merge, dismiss stale approvals, CODEOWNERS for workflow/policy/verifier/dependency/protected paths, no bypass for ordinary authors) - the full list is in the [trust model](docs/trust-model.md#required-github-setup). 5. Make `Runcap adjudicate` a required status check. -> The template ships with an all-zero placeholder SHA and is **intentionally not runnable until you insert the release SHA**. This is deliberate: the judge must be an immutable release commit that lives outside the candidate PR, so a malicious PR cannot rewrite its own judge. +> The template is pinned to the **Runcap v0.6.0** release commit. This is deliberate: the judge must be an immutable release commit that lives outside the candidate PR, so a malicious PR cannot rewrite its own judge. A reviewer sees one of two things: diff --git a/examples/runcap-adjudicate.yml b/examples/runcap-adjudicate.yml index 81c219b..2a3e7d6 100644 --- a/examples/runcap-adjudicate.yml +++ b/examples/runcap-adjudicate.yml @@ -22,8 +22,10 @@ # - GitHub-hosted runner, capped runtime, single self-sufficient required # check with no `needs:` on any upstream job. # -# Pin RUNCAP_ACTION_SHA to the commit a Runcap release tag points at. Resolve it -# with: gh api repos/kirder24-code/ai-agent-manager/git/refs/tags/vX.Y.Z --jq '.object.sha' +# This template is pinned to Runcap v0.6.0. When intentionally upgrading, replace +# the SHA with the exact commit a Runcap release tag points at. Resolve it with: +# gh api repos/kirder24-code/ai-agent-manager/git/refs/tags/vX.Y.Z --jq '.object.sha' +# Never use @v1 or another floating tag for the Proof Gate. Use a full immutable commit SHA. name: Runcap adjudicate on: @@ -48,10 +50,11 @@ jobs: with: node-version: 22 - # The judge: the Runcap action pinned by a full commit SHA. Replace the SHA - # below with the commit a published Runcap release tag points at. This is - # the ONLY code that decides the verdict, and it cannot come from the PR. + # The judge: the Runcap action pinned by a full commit SHA. This template is + # pinned to Runcap v0.6.0; when intentionally upgrading, replace the SHA below + # with the commit a published Runcap release tag points at. This is the ONLY + # code that decides the verdict, and it cannot come from the PR. - name: Runcap independent adjudication - uses: kirder24-code/ai-agent-manager@0000000000000000000000000000000000000000 # pin to a release SHA + uses: kirder24-code/ai-agent-manager@1eb87456333093c9fb8da6e9c21eef8d850891bc # Runcap v0.6.0 with: mode: adjudicate From 9a4b820160fe6a7ad16444336e4f8678310a402b Mon Sep 17 00:00:00 2001 From: "Kirill D." Date: Sun, 28 Jun 2026 14:53:53 -0600 Subject: [PATCH 2/2] docs: fix unsafe annotated-tag SHA-resolution guidance for Proof Gate pin Reading a tag ref's `.object.sha` returns the tag object SHA for an annotated tag, not the commit the Proof Gate must pin. Replace that gh-api guidance in README and examples/runcap-adjudicate.yml with the full release commit SHA plus `git rev-parse "vX.Y.Z^{}"` for local peeling, and add a Tier 3 regression assertion that neither doc reintroduces the unsafe pattern. Co-Authored-By: Claude Opus 4.7 --- README.md | 2 +- examples/runcap-adjudicate.yml | 8 ++++++-- scripts/adjudicate-test.mjs | 14 ++++++++++++++ 3 files changed, 21 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index bc496f2..7059ca5 100644 --- a/README.md +++ b/README.md @@ -337,7 +337,7 @@ Make the adjudication a required red/green PR check in your own repo: 1. Add `.runcap/mission.yaml` (the policy - see the example above). 2. Copy `examples/runcap-adjudicate.yml` into `.github/workflows/`. -3. The template is pinned to Runcap v0.6.0. When intentionally upgrading, replace the SHA with the exact commit SHA of the release you choose (resolve it with `gh api repos/kirder24-code/ai-agent-manager/git/refs/tags/vX.Y.Z --jq '.object.sha'`). Never use `@v1` or another floating tag for the Proof Gate. Use a full immutable commit SHA. +3. The template is pinned to Runcap v0.6.0. When intentionally upgrading, replace the SHA with the full 40-character target commit SHA shown for the GitHub Release you choose. For local verification in a clone of the Runcap repository, peel an annotated tag to its commit with `git rev-parse "vX.Y.Z^{}"`. Do not use an annotated tag object SHA - for the Proof Gate, pin the commit SHA that the release tag resolves to. Never use `@v1` or another floating tag for the Proof Gate. Use a full immutable commit SHA. 4. Configure the hardened GitHub branch profile (protected branch, required check, up-to-date-before-merge, dismiss stale approvals, CODEOWNERS for workflow/policy/verifier/dependency/protected paths, no bypass for ordinary authors) - the full list is in the [trust model](docs/trust-model.md#required-github-setup). 5. Make `Runcap adjudicate` a required status check. diff --git a/examples/runcap-adjudicate.yml b/examples/runcap-adjudicate.yml index 2a3e7d6..032a18b 100644 --- a/examples/runcap-adjudicate.yml +++ b/examples/runcap-adjudicate.yml @@ -23,8 +23,12 @@ # check with no `needs:` on any upstream job. # # This template is pinned to Runcap v0.6.0. When intentionally upgrading, replace -# the SHA with the exact commit a Runcap release tag points at. Resolve it with: -# gh api repos/kirder24-code/ai-agent-manager/git/refs/tags/vX.Y.Z --jq '.object.sha' +# the SHA with the full 40-character target commit SHA shown for the GitHub Release +# you choose. For local verification in a clone of the Runcap repository, peel an +# annotated tag to its commit with: +# git rev-parse "vX.Y.Z^{}" +# Do not use an annotated tag object SHA - for the Proof Gate, pin the commit SHA +# that the release tag resolves to. # Never use @v1 or another floating tag for the Proof Gate. Use a full immutable commit SHA. name: Runcap adjudicate diff --git a/scripts/adjudicate-test.mjs b/scripts/adjudicate-test.mjs index 336ad54..427f56f 100644 --- a/scripts/adjudicate-test.mjs +++ b/scripts/adjudicate-test.mjs @@ -304,6 +304,20 @@ check("reference workflow pins every action by a full 40-char commit SHA (no @v4 usesRefs.length > 0 && usesRefs.every((u) => /@[0-9a-f]{40}$/.test(u)), JSON.stringify(usesRefs)); check("reference workflow's judge is the released Runcap action, not workspace code", /uses:\s*kirder24-code\/ai-agent-manager@[0-9a-f]{40}/.test(wfText) && /mode:\s*adjudicate/.test(wfText), "released action judge"); +// SHA-resolution guidance must NOT teach the annotated-tag-object trap. Reading a +// tag ref's `.object.sha` returns the TAG OBJECT sha for an annotated tag, not the +// commit the Proof Gate must pin. The docs (workflow header comment AND README) must +// not contain that pattern, and must teach `git rev-parse "vX.Y.Z^{}"` instead. +const UNSAFE_SHA = /git\/refs\/tags\/[^\n]*--jq[^\n]*\.object\.sha/; +const readmeRaw = readFileSync(path.join(REPO_ROOT, "README.md"), "utf8"); +check("consumer template does not teach the unsafe `git/refs/tags ... --jq .object.sha` resolution", + !UNSAFE_SHA.test(wfRaw), "workflow header gh-api pattern"); +check("README does not teach the unsafe `git/refs/tags ... --jq .object.sha` resolution", + !UNSAFE_SHA.test(readmeRaw), "README gh-api pattern"); +check("consumer template teaches `git rev-parse \"vX.Y.Z^{}\"` to peel an annotated tag to its commit", + /git rev-parse "vX\.Y\.Z\^\{\}"/.test(wfRaw), "workflow rev-parse guidance"); +check("README teaches `git rev-parse \"vX.Y.Z^{}\"` to peel an annotated tag to its commit", + /git rev-parse "vX\.Y\.Z\^\{\}"/.test(readmeRaw), "README rev-parse guidance"); // --- 22. the judge is the adjudicator's OWN code, not the PR's bin ----------- // A head PR that rewrites bin/runcap.mjs to always print PASS, or rewrites