Skip to content

docs: document OIDC immutability constraint and exempt claude.yml from SHA pinning#159

Open
don-petry wants to merge 1 commit intomainfrom
fix/claude-oidc-immutability-standard
Open

docs: document OIDC immutability constraint and exempt claude.yml from SHA pinning#159
don-petry wants to merge 1 commit intomainfrom
fix/claude-oidc-immutability-standard

Conversation

@don-petry
Copy link
Copy Markdown
Contributor

@don-petry don-petry commented Apr 21, 2026

Summary

Documents the Anthropic OIDC invariant that caused a multi-repo incident on 2026-04-19/20, adds structural guards, and closes the policy gap that caused compliance agents to open PRs modifying claude.yml.

Root cause of incident

Agent-generated compliance PRs (SHA-pinning the uses: line in claude.yml) produced PR branches where claude.yml differed from main. Anthropic's OIDC token endpoint rejected these with 401 Workflow validation failed, preventing Claude Code from running on those PRs. A human fix attempt merged a broken path, requiring an emergency revert across 4 repos.

Changes

  • standards/workflows/claude.yml — Expands the AGENTS header to explain the OIDC constraint and adds paths-ignore: ['.github/workflows/claude.yml'] to prevent the workflow from firing on PRs that only change this file (structural self-enforcement).
  • standards/ci-standards.md — Adds an OIDC constraint callout to §4 (Claude Code) and a new "Exception: Internal Reusable Workflow References" subsection to the Action Pinning Policy, explicitly exempting internal reusable workflow refs from SHA pinning.
  • standards/agent-standards.md — Adds a "Compliance Exemptions" section listing claude.yml and agent-shield.yml as files agents must never open PRs against, with the reason documented.

Follow-up (separate)

Close the 4 stale open SHA-pinning PRs in markets, ContentTwin, TalkTerm, and bmad-bgreat-suite with a comment pointing to this standards update.

Summary by CodeRabbit

  • Documentation
    • Established immutable workflow file standards with enforcement guidelines.
    • Added security constraints for workflow validation and OIDC token exchanges.
    • Updated workflow triggers with path-ignore rules to prevent unintended executions on protected file changes.

…m SHA pinning

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 <noreply@anthropic.com>
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 21, 2026

📝 Walkthrough

Walkthrough

This pull request establishes policies and configuration for workflow file immutability. It adds documentation defining two GitHub Actions workflows (.github/workflows/claude.yml and .github/workflows/agent-shield.yml) as structurally immutable, specifies security constraints for byte-for-byte identity enforcement, and updates the Claude workflow trigger to skip runs on self-modifications via paths-ignore.

Changes

Cohort / File(s) Summary
Agent and CI Standards Documentation
standards/agent-standards.md, standards/ci-standards.md
Added "Compliance Exemptions" section defining workflow files as immutable; added security constraint requiring .github/workflows/claude.yml byte-for-byte identity and OIDC token failure on modifications; clarified reusable workflow reference exception allowing tag refs instead of SHA pins for petry-projects/.github workflows.
Claude Workflow Configuration
standards/workflows/claude.yml
Updated pull_request trigger with paths-ignore rule to exclude .github/workflows/claude.yml modifications from triggering the workflow; added inline comments documenting immutability invariant.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~12 minutes

Possibly related PRs

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main change: documenting an OIDC immutability constraint and exempting claude.yml from SHA pinning, which directly corresponds to the core objectives and changes made across all three files.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/claude-oidc-immutability-standard

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@sonarqubecloud
Copy link
Copy Markdown

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
standards/workflows/claude.yml (1)

34-39: ⚠️ Potential issue | 🟡 Minor

paths-ignore only affects pull_request — confirm that's the intended scope.

paths-ignore under pull_request: prevents that trigger from running on PRs whose changeset is limited to claude.yml, which matches the stated goal. The other triggers (issue_comment, pull_request_review_comment, issues, check_run) are unaffected, which is correct — those don't carry PR-head workflow files anyway.

One edge case: a PR that modifies claude.yml plus other files will still trigger (GitHub's paths-ignore only skips when all changed files match). Such PRs will still hit the 401 at token exchange. The header text is clear enough about this, but it may be worth an explicit sentence in the header noting that paths-ignore is a narrow guard, not a complete shield.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@standards/workflows/claude.yml` around lines 34 - 39, Update the workflow
header comment to explicitly state that the paths-ignore setting under the
pull_request trigger only prevents the workflow from running when the PR's
entire changeset is limited to claude.yml and does not protect PRs that modify
claude.yml plus other files; reference the pull_request block and the
paths-ignore key, and briefly note that other triggers (issue_comment,
pull_request_review_comment, issues, check_run) are unaffected so they still run
as configured.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@standards/agent-standards.md`:
- Around line 17-31: Add a machine-readable exemption list and wire it into the
existing compliance check so agents can’t open PRs that modify those immutable
workflow files: create a small JSON or YAML file (e.g., a single-key list
containing ".github/workflows/claude.yml" and
".github/workflows/agent-shield.yml" and any future entries) and update
scripts/compliance-audit.sh to read that file and reject PRs touching any listed
paths; also add a one-line reference in agent-standards.md next to the exemption
table pointing to this machine-readable source (and mention that the canonical
source of truth is the standards/workflows/ adoption process) so humans and
automation reference the same list.

In `@standards/ci-standards.md`:
- Around line 238-253: The MD lint failures are caused by the fenced code block
inside the OIDC security constraint blockquote: add a language to the fence
(e.g., ```text```), ensure there is a blank line inside the blockquote before
and after the fenced block (prefix those blank lines with > to satisfy
MD031/MD040), and remove the extra blank line that separates this blockquote
from the next callout so it isn't split (or if the split is intentional, keep
the blank line but update markdownlint config to allow MD028); update the “OIDC
security constraint — `claude.yml` is immutable on PR branches.” block
accordingly.
- Around line 756-779: The check_action_pinning() audit currently flags tag refs
for internal reusable workflows; update its grep/filter logic so it explicitly
excludes petry-projects/.github reusable-workflow tag refs (e.g. patterns
matching petry-projects/.github/**@(v1|main)); modify the filter used in
check_action_pinning() to add a negative match or whitelist for
petry-projects/.github/**@(v1|main) so those tag refs are skipped and not
reported or used to create compliance PRs.

---

Outside diff comments:
In `@standards/workflows/claude.yml`:
- Around line 34-39: Update the workflow header comment to explicitly state that
the paths-ignore setting under the pull_request trigger only prevents the
workflow from running when the PR's entire changeset is limited to claude.yml
and does not protect PRs that modify claude.yml plus other files; reference the
pull_request block and the paths-ignore key, and briefly note that other
triggers (issue_comment, pull_request_review_comment, issues, check_run) are
unaffected so they still run as configured.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 6d18e6ec-5bf4-4a52-9a3c-86868f304db0

📥 Commits

Reviewing files that changed from the base of the PR and between ba10e66 and f96a9c6.

📒 Files selected for processing (3)
  • standards/agent-standards.md
  • standards/ci-standards.md
  • standards/workflows/claude.yml

Comment on lines +17 to +31
## 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.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick | 🔵 Trivial

LGTM — exemption section is well-scoped.

Clear rationale per file, and the "adopt verbatim / updated only via standards PR" instruction lines up with the Tier 1 stub contract in ci-standards.md. The LanguageTool "GitHub" capitalization hints on lines 25/26/29 are false positives — these are literal path/repo identifiers.

Minor suggestion: consider adding a machine-readable signal (e.g., a known list consumed by scripts/compliance-audit.sh) so the exemption is enforced at PR-open time rather than relying on agents reading this doc. Otherwise an agent that skips the doc will still attempt a compliance PR against claude.yml.

🧰 Tools
🪛 LanguageTool

[uncategorized] ~25-~25: The official name of this software platform is spelled with a capital “H”.
Context: ... | File | Reason | |------|--------| | .github/workflows/claude.yml | Anthropic OIDC ...

(GITHUB)


[uncategorized] ~26-~26: The official name of this software platform is spelled with a capital “H”.
Context: ...; Claude Code cannot run on that PR | | .github/workflows/agent-shield.yml | Security ...

(GITHUB)


[uncategorized] ~29-~29: The official name of this software platform is spelled with a capital “H”.
Context: ...ted only by merging a standards PR from petry-projects/.github, which propagates to all repos via the...

(GITHUB)

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@standards/agent-standards.md` around lines 17 - 31, Add a machine-readable
exemption list and wire it into the existing compliance check so agents can’t
open PRs that modify those immutable workflow files: create a small JSON or YAML
file (e.g., a single-key list containing ".github/workflows/claude.yml" and
".github/workflows/agent-shield.yml" and any future entries) and update
scripts/compliance-audit.sh to read that file and reject PRs touching any listed
paths; also add a one-line reference in agent-standards.md next to the exemption
table pointing to this machine-readable source (and mention that the canonical
source of truth is the standards/workflows/ adoption process) so humans and
automation reference the same list.

Comment thread standards/ci-standards.md
Comment on lines +238 to +253
> **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.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Fix markdownlint failures in the OIDC callout — CI is red.

The fenced block inside the blockquote violates MD031 (no surrounding blank lines inside the blockquote), MD040 (missing language), and MD028 (blank line at 253 between this blockquote and the next breaks them into two). Pipeline is failing on MD031.

🛠️ Proposed fix
 > **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:
-> ```
+>
+> ```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

If the blockquote separation at line 253 is intentional (two distinct callouts), keep the blank line but ensure markdownlint config accepts MD028 — the current default does not.

🧰 Tools
🪛 GitHub Actions: CI

[error] 243-243: markdownlint-cli2 reported MD031/blanks-around-fences: Fenced code blocks should be surrounded by blank lines [Context: "> ```"].

🪛 GitHub Check: Lint

[failure] 253-253: Blank line inside blockquote
standards/ci-standards.md:253 MD028/no-blanks-blockquote Blank line inside blockquote https://github.com/DavidAnson/markdownlint/blob/v0.40.0/doc/md028.md


[failure] 247-247: Fenced code blocks should be surrounded by blank lines
standards/ci-standards.md:247 MD031/blanks-around-fences Fenced code blocks should be surrounded by blank lines [Context: "> ```"] https://github.com/DavidAnson/markdownlint/blob/v0.40.0/doc/md031.md


[failure] 243-243: Fenced code blocks should have a language specified
standards/ci-standards.md:243 MD040/fenced-code-language Fenced code blocks should have a language specified [Context: "```"] https://github.com/DavidAnson/markdownlint/blob/v0.40.0/doc/md040.md


[failure] 243-243: Fenced code blocks should be surrounded by blank lines
standards/ci-standards.md:243 MD031/blanks-around-fences Fenced code blocks should be surrounded by blank lines [Context: "> ```"] https://github.com/DavidAnson/markdownlint/blob/v0.40.0/doc/md031.md

🪛 LanguageTool

[uncategorized] ~239-~239: The official name of this software platform is spelled with a capital “H”.
Context: ...thropic's token endpoint validates that .github/workflows/claude.yml on > a PR branch ...

(GITHUB)


[uncategorized] ~248-~248: The official name of this software platform is spelled with a capital “H”.
Context: ...R. Agents must not open PRs that modify > .github/workflows/claude.yml. The caller stub ...

(GITHUB)

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@standards/ci-standards.md` around lines 238 - 253, The MD lint failures are
caused by the fenced code block inside the OIDC security constraint blockquote:
add a language to the fence (e.g., ```text```), ensure there is a blank line
inside the blockquote before and after the fenced block (prefix those blank
lines with > to satisfy MD031/MD040), and remove the extra blank line that
separates this blockquote from the next callout so it isn't split (or if the
split is intentional, keep the blank line but update markdownlint config to
allow MD028); update the “OIDC security constraint — `claude.yml` is immutable
on PR branches.” block accordingly.

Comment thread standards/ci-standards.md
Comment on lines +756 to +779
### 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.**

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
rg -nP -C3 'check_action_pinning|unpinned-actions' scripts/compliance-audit.sh

Repository: petry-projects/.github

Length of output: 998


🏁 Script executed:

sed -n '186,250p' scripts/compliance-audit.sh

Repository: petry-projects/.github

Length of output: 2532


🏁 Script executed:

rg -n 'petry-projects.*\.github' scripts/compliance-audit.sh | head -20

Repository: petry-projects/.github

Length of output: 2479


🏁 Script executed:

rg -n '@v1|@main' scripts/compliance-audit.sh | grep -i 'filter\|exempt\|exclude'

Repository: petry-projects/.github

Length of output: 48


🏁 Script executed:

sed -n '756,779p' standards/ci-standards.md

Repository: petry-projects/.github

Length of output: 1137


Add whitelist for petry-projects/.github reusable workflow tag refs to check_action_pinning().

The check_action_pinning() function currently lacks a filter to exempt petry-projects/.github/** reusable workflows tagged with @v1 or @main. Without this whitelist, the audit will flag these exempted refs as unpinned and create compliance PRs attempting to pin them to SHA, defeating the purpose of the exemption documented in ci-standards.md (lines 756–779). Update the grep filter to exclude petry-projects/.github/**@(v1|main) patterns.

🧰 Tools
🪛 LanguageTool

[uncategorized] ~758-~758: The official name of this software platform is spelled with a capital “H”.
Context: ... Reusable Workflow References Calls to petry-projects/.github reusable workflows use tag references ...

(GITHUB)


[uncategorized] ~770-~770: The official name of this software platform is spelled with a capital “H”.
Context: ...ic's OIDC token endpoint validates that .github/workflows/claude.yml on a PR branch is...

(GITHUB)


[uncategorized] ~775-~775: The official name of this software platform is spelled with a capital “H”.
Context: ...annot run on that PR. The @v1 tag on petry-projects/.github is managed deliberately (bumped only o...

(GITHUB)

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@standards/ci-standards.md` around lines 756 - 779, The check_action_pinning()
audit currently flags tag refs for internal reusable workflows; update its
grep/filter logic so it explicitly excludes petry-projects/.github
reusable-workflow tag refs (e.g. patterns matching
petry-projects/.github/**@(v1|main)); modify the filter used in
check_action_pinning() to add a negative match or whitelist for
petry-projects/.github/**@(v1|main) so those tag refs are skipped and not
reported or used to create compliance PRs.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant