From f96a9c6a3dbda8487f371dbb161358b9cbebc9f4 Mon Sep 17 00:00:00 2001 From: DJ Date: Tue, 21 Apr 2026 05:08:24 -0700 Subject: [PATCH] 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 3f5cad4..0d54150 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 e585707..c5e99ec 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 916a6da..102722a 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: