Secret-rules PR gate — operations playbook

Implemented by: Phase 4 of the token-audit plan (#7882).
Workflow file: .github/workflows/secret-rules-pr-gate.yml
Check script: scripts/secret-rules-pr-gate.sh


What it is

A deterministic CI check that runs on every PR touching the files that define secret-protection behaviour: CLAUDE.md, .claude/**, infra/agent-prompts/**, and standards/**. It blocks PRs that lower the protection level and passes neutral changes.

No LLM probe runs here — that lives in the weekly audit (.claude/commands/weekly-token-audit.md).


What the gate checks

#CheckFailure means
1Hook file inventory — pre-bash-safety.sh, pre-read-safety.sh, pre-grep-safety.sh exist in .claude/hooks/A hook file was deleted or renamed
2Hooks wired in settings.json — all three hooks appear in .claude/settings.json under PreToolUseA hook was de-registered from the Claude Code trigger
3Sentinel hash valid — standards/common/secret-rules-core.md hash in line 1 matches sha256sum(lines 2+)The file was edited without refreshing the hash
4Sentinel presence — required files contain <!-- SECRET-RULES:v1 -->The sentinel marker was removed from a worker prompt or skill
5CLAUDE.md size ≤ 80 KBThe always-loaded context grew past the token threshold
6Hook provocations — hooks actually block known-bad inputsA hook script was broken (exits 0 when it should exit 1)

How to pass the gate

After editing standards/common/secret-rules-core.md

The file carries a content-hash sentinel on line 1. Any body edit (lines 2+) invalidates it:

bash scripts/update-secret-rules-hash.sh
git add standards/common/secret-rules-core.md

Verify locally before pushing:

bash scripts/secret-rules-pr-gate.sh

After modifying a hook file

Ensure the hook still blocks its known-bad cases:

bash scripts/secret-rules-pr-gate.sh

If a provocation test fails, the hook logic changed in a way that weakens it. Review the diff and restore the intended behaviour.

After modifying .claude/settings.json

The gate checks that pre-bash-safety, pre-read-safety, and pre-grep-safety still appear under PreToolUse. Ensure you did not remove or rename a hook command.

After editing a sentinel-required file

Sentinel-required files (infra/agent-prompts/worker-issue.md, infra/agent-prompts/review-pr-worker.md, .claude/commands/weekly-supabase-advisor-audit.md, .claude/commands/weekly-token-audit.md) must contain the line:

<!-- SECRET-RULES:v1 -->

If you removed it by accident, restore it. If you intentionally removed the rules block, do not remove the sentinel marker — leave it in place and update the block’s content instead. The sentinel marks that the file was reviewed for secret rules compliance, not that a specific text block is present.

If CLAUDE.md exceeds 80 KB

The limit exists because CLAUDE.md is loaded into every session’s context, directly raising the first-turn token count. To reduce its size:

  1. Move rarely-used sections to separate files under docs/playbooks/ and add a one-line reference link in CLAUDE.md.
  2. Consolidate duplicate guidance (e.g. two descriptions of the same SOPS pattern).
  3. Remove stale content (decommissioned services, resolved incidents).

Do not raise the 80 KB limit without a design decision — that threshold exists to protect the 55k-median-start-context alarm from the weekly token audit.


Branch protection

The gate is most valuable as a required status check on main. Until the workflow has run at least once on a PR to main, GitHub won’t list secret-rules-check as an available check name. Run it once manually or wait for the first qualifying PR, then add it:

# Step 1 — read the current required checks (must be preserved in Step 2's array)
gh api repos/radieu/p24-infra/branches/main/protection/required_status_checks \
  --jq '.checks[]'
 
# Step 2 — add secret-rules-check alongside the existing checks
# Replace EXISTING_CHECK_1 / EXISTING_CHECK_2 with the names from Step 1.
gh api repos/radieu/p24-infra/branches/main/protection/required_status_checks \
  --method PATCH \
  --input - <<'JSON'
{
  "strict": false,
  "checks": [
    {"context": "EXISTING_CHECK_1", "app_id": -1},
    {"context": "EXISTING_CHECK_2", "app_id": -1},
    {"context": "secret-rules-check", "app_id": -1}
  ]
}
JSON

Note: GitHub’s PATCH /required_status_checks replaces the whole list. Always include the existing checks in the array, or they will be removed.

For et-operational-platform, the same workflow needs to be added to that repo (sync is out of scope for this issue). The branch protection command is the same with radieu/et-operational-platform and the target branch.


Running the check locally

# From the repo root
bash scripts/secret-rules-pr-gate.sh

Exit 0 = all checks passed.
Exit 1 = one or more checks failed (details printed to stdout).


Adding a new sentinel-required file

  1. Add the file path to the SENTINEL_FILES array in:
    • scripts/secret-rules-pr-gate.sh
    • .claude/commands/weekly-token-audit.md (Phase 2 Part B section)
  2. Ensure the file contains <!-- SECRET-RULES:v1 --> at a reasonable location.
  3. Run bash scripts/secret-rules-pr-gate.sh to verify.
  4. Commit both the sentinel-required file and the updated arrays together.

Incident response

Gate fails on a push to main (protection was lowered):

  1. The workflow opens a bug issue automatically and fires a Discord alert.
  2. Identify which commit introduced the failure (git log --oneline -5).
  3. Restore the protection (re-add the sentinel, re-wire the hook, etc.).
  4. Open a PR with the fix — the gate must pass on the fix PR before merging.
  5. Close the auto-opened bug issue.

Gate fails on a PR that should be neutral:

  1. Run bash scripts/secret-rules-pr-gate.sh locally against your branch.
  2. The script prints which specific check(s) failed.
  3. Apply the remediation above for the relevant check.
  4. If the failure is a false positive (the gate’s logic is wrong), open a separate issue — do not bypass the check in the PR under review.