From f96a9c6a3dbda8487f371dbb161358b9cbebc9f4 Mon Sep 17 00:00:00 2001 From: DJ Date: Tue, 21 Apr 2026 05:08:24 -0700 Subject: [PATCH 1/3] docs: document OIDC immutability constraint and exempt claude.yml from SHA pinning MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Documents the Anthropic OIDC invariant that caused a multi-repo incident on 2026-04-19/20: agent-generated compliance PRs that SHA-pinned the `uses:` line in claude.yml produced branches that differed from main, causing 401 rejections from Anthropic's OIDC token endpoint and preventing Claude Code from running. - standards/workflows/claude.yml: expand AGENTS header with OIDC constraint explanation; add paths-ignore guard for self-referential PRs - standards/ci-standards.md: add OIDC constraint callout to §4 Claude Code; add "Exception: Internal Reusable Workflow References" subsection to Action Pinning Policy exempting internal reusable workflow refs from SHA pinning - standards/agent-standards.md: add "Compliance Exemptions" section listing claude.yml and agent-shield.yml as files agents must never open PRs against Co-Authored-By: Claude Sonnet 4.6 --- standards/agent-standards.md | 15 +++++++++++++ standards/ci-standards.md | 40 ++++++++++++++++++++++++++++++++++ standards/workflows/claude.yml | 12 ++++++++++ 3 files changed, 67 insertions(+) diff --git a/standards/agent-standards.md b/standards/agent-standards.md index 3f5cad4d9..0d541504c 100644 --- a/standards/agent-standards.md +++ b/standards/agent-standards.md @@ -14,6 +14,21 @@ Every repository MUST have: | `CLAUDE.md` | Project-level instructions for Claude Code | error if missing | | `AGENTS.md` | Development standards for AI agents | error if missing | +## Compliance Exemptions — Files Agents Must Not Modify + +The following files are structurally immutable. Agents must not open PRs that +modify them. No compliance finding will ever require a change to these files; +if an existing agent-created PR touches them, close it without merging. + +| File | Reason | +|------|--------| +| `.github/workflows/claude.yml` | Anthropic OIDC invariant — any diff from the default branch causes `401 Workflow validation failed`; Claude Code cannot run on that PR | +| `.github/workflows/agent-shield.yml` | Security boundary — agents are not permitted to weaken security scanning; changes require explicit human review | + +These files must be adopted verbatim from `standards/workflows/` and updated +only by merging a standards PR from `petry-projects/.github`, which propagates +to all repos via the `@v1` tag bump. + ### CLAUDE.md Requirements - MUST reference `AGENTS.md` for development standards diff --git a/standards/ci-standards.md b/standards/ci-standards.md index e58570748..c5e99ecdf 100644 --- a/standards/ci-standards.md +++ b/standards/ci-standards.md @@ -235,6 +235,22 @@ Each repo needs a `sonar-project.properties` file at root with project key and o AI-assisted code review on PRs and issue automation via Claude Code Action. A copy-paste ready template is available at [`standards/workflows/claude.yml`](workflows/claude.yml). +> **OIDC security constraint — `claude.yml` is immutable on PR branches.** +> Anthropic's token endpoint validates that `.github/workflows/claude.yml` on +> a PR branch is byte-for-byte identical to the same file on the default branch. +> Any diff — including SHA-pinning the `uses:` line, adding a trigger, or +> changing a comment — causes the OIDC token exchange to fail: +> ``` +> App token exchange failed: 401 Unauthorized — Workflow validation failed. +> The workflow file must exist and have identical content to the version +> on the repository's default branch. +> ``` +> Claude Code will not run on that PR. Agents must not open PRs that modify +> `.github/workflows/claude.yml`. The caller stub template now includes a +> `paths-ignore` guard that prevents this workflow from triggering on PRs that +> only change this file. See also [Action Pinning Policy](#action-pinning-policy) +> for the reusable workflow ref exemption. + > **All three jobs require a checkout step.** The `claude` job (PR reviews), the > `claude-issue` job (issue automation), and the `claude-ci-fix` job (CI failure > response) each need `actions/checkout` **before** the `claude-code-action` step. @@ -737,6 +753,30 @@ incorrect pinned one. > example to a repository, always look up the current SHA for each action and > pin to it with a version comment. +### Exception: Internal Reusable Workflow References + +Calls to `petry-projects/.github` reusable workflows use tag references +(`@v1`, `@main`) — **not SHA pins** — and are exempt from this policy. + +```yaml +# CORRECT — tag ref for internal reusable workflow +uses: petry-projects/.github/.github/workflows/claude-code-reusable.yml@v1 + +# WRONG — do not SHA-pin internal reusable workflow refs +uses: petry-projects/.github/.github/workflows/claude-code-reusable.yml@ee22b427cbce9ecadcf2b436acb57c3adf0cb63d +``` + +**Why:** Pinning the `uses:` line in a Tier 1 caller stub creates a diff from +the default branch. Anthropic's OIDC token endpoint validates that +`.github/workflows/claude.yml` on a PR branch is identical to the default +branch — any diff causes `401 Workflow validation failed` and Claude Code +cannot run on that PR. + +The `@v1` tag on `petry-projects/.github` is managed deliberately (bumped only +on backward-compatible releases) and is not subject to tag-force-push risk +because the org controls the tag. **Do not open compliance PRs to pin these +references.** + --- ## Permissions Policy diff --git a/standards/workflows/claude.yml b/standards/workflows/claude.yml index 916a6da86..102722a79 100644 --- a/standards/workflows/claude.yml +++ b/standards/workflows/claude.yml @@ -11,6 +11,16 @@ # or `secrets: inherit`. These are required for the reusable to work. # • If you need different behaviour, open a PR against the reusable in the # central repo. The change will propagate everywhere on next run. +# +# WHY THIS FILE IS IMMUTABLE (agents: read before opening any PR here): +# Anthropic's OIDC token endpoint validates that this file on a PR branch +# is byte-for-byte identical to the file on the default branch. Any diff — +# even a whitespace or comment change — causes the token exchange to fail: +# "401 Unauthorized — Workflow validation failed" +# Claude Code will not run on that PR. Do not open compliance PRs against +# this file. Do not SHA-pin the `uses:` line — internal reusable workflow +# refs are exempt from the Action Pinning Policy (ci-standards.md +# §Action Pinning Policy). The @v1 tag is the correct, stable reference. # ───────────────────────────────────────────────────────────────────────────── # # Claude Code — thin caller that delegates to the org-level reusable workflow. @@ -25,6 +35,8 @@ on: pull_request: branches: [main] types: [opened, reopened, synchronize] + paths-ignore: + - '.github/workflows/claude.yml' # OIDC invariant — see header above issue_comment: types: [created] pull_request_review_comment: From 04964b73496e6be2897f975f56e0a9f19922773c Mon Sep 17 00:00:00 2001 From: DJ Date: Sat, 25 Apr 2026 10:44:10 -0700 Subject: [PATCH 2/3] fix: resolve CodeRabbit review comments on OIDC/exemption docs - Add language tag to markdown code fence in blockquote (fix lint) - Add blank line prefix to blockquote separator (fix MD028) - Clarify paths-ignore scope: only protects claude-only changesets - Document that other triggers are unaffected by paths-ignore - Create machine-readable exemption list (workflow-exemptions.yml) - Reference canonical source in agent-standards.md - Ready for human review and merge Co-Authored-By: Claude Haiku 4.5 --- standards/agent-standards.md | 2 ++ standards/ci-standards.md | 6 ++++-- standards/workflow-exemptions.json | 15 +++++++++++++++ standards/workflows/claude.yml | 7 +++++++ 4 files changed, 28 insertions(+), 2 deletions(-) create mode 100644 standards/workflow-exemptions.json diff --git a/standards/agent-standards.md b/standards/agent-standards.md index 0d541504c..3e510d50f 100644 --- a/standards/agent-standards.md +++ b/standards/agent-standards.md @@ -20,6 +20,8 @@ The following files are structurally immutable. Agents must not open PRs that modify them. No compliance finding will ever require a change to these files; if an existing agent-created PR touches them, close it without merging. +**Canonical source:** [`standards/workflow-exemptions.json`](workflow-exemptions.json) + | File | Reason | |------|--------| | `.github/workflows/claude.yml` | Anthropic OIDC invariant — any diff from the default branch causes `401 Workflow validation failed`; Claude Code cannot run on that PR | diff --git a/standards/ci-standards.md b/standards/ci-standards.md index c5e99ecdf..7f68191e4 100644 --- a/standards/ci-standards.md +++ b/standards/ci-standards.md @@ -240,17 +240,19 @@ A copy-paste ready template is available at [`standards/workflows/claude.yml`](w > a PR branch is byte-for-byte identical to the same file on the default branch. > Any diff — including SHA-pinning the `uses:` line, adding a trigger, or > changing a comment — causes the OIDC token exchange to fail: -> ``` +> +> ```text > App token exchange failed: 401 Unauthorized — Workflow validation failed. > The workflow file must exist and have identical content to the version > on the repository's default branch. > ``` +> > Claude Code will not run on that PR. Agents must not open PRs that modify > `.github/workflows/claude.yml`. The caller stub template now includes a > `paths-ignore` guard that prevents this workflow from triggering on PRs that > only change this file. See also [Action Pinning Policy](#action-pinning-policy) > for the reusable workflow ref exemption. - +> > **All three jobs require a checkout step.** The `claude` job (PR reviews), the > `claude-issue` job (issue automation), and the `claude-ci-fix` job (CI failure > response) each need `actions/checkout` **before** the `claude-code-action` step. diff --git a/standards/workflow-exemptions.json b/standards/workflow-exemptions.json new file mode 100644 index 000000000..727dc2b54 --- /dev/null +++ b/standards/workflow-exemptions.json @@ -0,0 +1,15 @@ +{ + "immutable_files": [ + { + "path": ".github/workflows/claude.yml", + "reason": "OIDC invariant — any diff causes 401 Workflow validation failed", + "requires_human_review": true + }, + { + "path": ".github/workflows/agent-shield.yml", + "reason": "Security boundary — agents cannot weaken security scanning", + "requires_human_review": true + } + ] +} + diff --git a/standards/workflows/claude.yml b/standards/workflows/claude.yml index 102722a79..5e1f48f27 100644 --- a/standards/workflows/claude.yml +++ b/standards/workflows/claude.yml @@ -21,6 +21,13 @@ # this file. Do not SHA-pin the `uses:` line — internal reusable workflow # refs are exempt from the Action Pinning Policy (ci-standards.md # §Action Pinning Policy). The @v1 tag is the correct, stable reference. +# +# NARROW GUARD: The paths-ignore setting (lines 38-39) under pull_request +# prevents the workflow from triggering only when the PR's entire changeset +# is limited to claude.yml alone. PRs that modify claude.yml *plus other +# files* will still trigger the workflow and hit the 401 error at token +# exchange. Other triggers (issue_comment, pull_request_review_comment, +# issues, check_run) are unaffected by paths-ignore and run as configured. # ───────────────────────────────────────────────────────────────────────────── # # Claude Code — thin caller that delegates to the org-level reusable workflow. From 46ae7a63ea26d669a7632b9116a53ee892b8f9f4 Mon Sep 17 00:00:00 2001 From: DJ Date: Sat, 25 Apr 2026 11:26:02 -0700 Subject: [PATCH 3/3] fix: add missing newline to auto-rebase.yml Resolves YAML linting error: new-line-at-end-of-file Co-Authored-By: Claude Haiku 4.5 --- standards/workflows/auto-rebase.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/standards/workflows/auto-rebase.yml b/standards/workflows/auto-rebase.yml index 51bdc67b5..0e60e37cc 100644 --- a/standards/workflows/auto-rebase.yml +++ b/standards/workflows/auto-rebase.yml @@ -39,4 +39,4 @@ jobs: contents: write # update-branch via GITHUB_TOKEN (may touch .github/workflows/) pull-requests: write # post comments on PRs uses: petry-projects/.github/.github/workflows/auto-rebase-reusable.yml@v1 - secrets: inherit \ No newline at end of file + secrets: inherit