feat(ci): detect paperclip-production environment protection drift (BLO-22329) - #1355
Conversation
|
🔗 Paperclip issue: BLO-22329 |
1 similar comment
|
🔗 Paperclip issue: BLO-22329 |
|
Hey @allyblockcast[bot]! Before this PR can be reviewed, a few things need attention: Missing or incomplete:
Once updated, push a new commit and these checks will re-run automatically. — commitperclip |
|
@ally please review at head This adds a scheduled detector for drift in the Highest-value review focus, in order:
Note: the two red checks on this head were a single ARC runner eviction in |
|
@ally please review at head 7b84cd3 — ci: detect paperclip-production environment protection drift. Focus on false-positive drift alerts from benign API shape changes. Context: the original review request on this PR was lost during the codex provider outage (BLO-27123) — codex |
…LO-22329) The `paperclip-production` GitHub environment gates production `helm upgrade`. Its protection has been recorded as correct by the board and been untrue three times: 2026-07-31 (never applied), 2026-08-04 (lapsed), and 2026-08-08 (widened under a disclosed incident override whose "restored immediately after" step never ran). Each went unnoticed for 2-6 days because a closed approval card was treated as evidence the control existed and nothing re-read the artifact. Adds a twice-daily guard that re-reads the live environment and asserts the shape ratified on approval b75f8156. Two design points worth calling out: - The reviewer set is compared by MEMBERSHIP, not for non-emptiness. The 2026-08-08 drift added `kkroo` (a repo admin) as a third reviewer and flipped can_admins_bypass to true; together those route around prevent_self_review. A "does a required_reviewers rule exist?" check passes that shape, so it would have missed the very drift that is live today. - A red workflow run is not the alerting path — it only emails the last committer, which is the failure mode being fixed. Drift is pushed to Alertmanager as severity=critical, which is what the route tree matches for the slack-relay receiver. Delivery failure is fatal and loud: silently failing to alert would recreate the defect this check exists to close. Exit codes are load-bearing so "drift" is distinguishable from "I could not check" — an unreadable environment alerts under its own alertname and is never reported as compliant. Verified against the live environment 2026-08-14T08:46Z (exit 1, naming both the kkroo membership drift and can_admins_bypass), against a compliant fixture (exit 0), and with a deliberately invalid token (exit 2, status=unreadable). Co-Authored-By: Claude <noreply@anthropic.com>
7b84cd3 to
8d06e92
Compare
|
@ally please review at head This is the third delivery attempt on this PR (markers at Review focus:
|
Thinking Path
Linked Issues or Issue Description
Refs BLO-22329 (this PR is the detector it specifies)
Refs BLO-18577 (the environment being guarded)
Related PRs searched — no duplicates. The nearest prior art is #1340 (hourly master lockfile drift monitor) and #1273; both are dependency-drift, not environment-protection, and neither covers this artifact. See "Risks" for why this PR deliberately does not copy #1340's alerting approach.
What Changed
.github/scripts/check-production-environment-protection.mjs— reads the live environment and asserts the shape ratified on approvalb75f8156: arequired_reviewersrule withprevent_self_review: true,can_admins_bypass: false, anddeployment_branch_policy.protected_branches: true. Exit codes are load-bearing:0compliant,1drift,2unreadable.kkroo(a repo admin) as a third reviewer alongsidecan_admins_bypass: true; together those route aroundprevent_self_review. A "does arequired_reviewersrule exist?" check passes that shape — so the obvious implementation would miss the drift that is live right now. Changing the ratified set is deliberately a code change, so the PR is the audit trail the three silent edits lacked..github/scripts/post-environment-protection-alert.mjs— pushes the drift to Alertmanager asseverity: critical, which is what the live route tree matches to theslack-relayreceiver. Alert delivery failure is fatal and loud..github/workflows/production-environment-protection-guard.yml— twice-daily schedule plusworkflow_dispatch; mints the App token via the existingget-bot-token.mjsidiom..github/workflows/pr.yml— wires both test files into thepolicyjob so they actually guard.Verification
All run locally 2026-08-14T08:46–08:55Z.
Unit tests — 19/19 pass:
Against the LIVE environment — exit 1, both violations named:
Against a compliant fixture — exit 0,
violations: [].Unreadable paths — never reported as compliant:
EXIT=2, summarystatus: unreadable, 401 surfaced verbatimEXIT=2Token choice: the default Actions
GITHUB_TOKENdoes not carry environments read; theallyblockcastApp installation token does — verified live this run againstGET /repos/Blockcast/paperclip/environments/paperclip-production.Risks
Low risk to anything existing — this adds a read-only scheduled job and touches no deploy path. It cannot block a release.
Two things a reviewer should push back on if they disagree:
Runner → Alertmanager reachability is unproven. I could not verify from my environment that an ARC runner can reach
alertmanager.monitoring.svc.cluster.local:9093. Themonitoringnamespace has adefault-deny-allNetworkPolicy whose only ingress allows are intra-namespace andgateway-system, yet a probe from thepaperclipnamespace returned 200 — so enforcement does not match the policy text, and I did not want to build on a guess. Mitigated by making delivery failure fatal and loud rather than silent, so the first run reports it honestly instead of pretending to alert.ALERTMANAGER_URLis overridable via repo vars.This deliberately diverges from ci(lockfile): add hourly master lockfile-overrides drift monitor (BLO-24150) #1340's precedent. That monitor alerts "via Actions notifications". BLO-22329 explicitly rules that out — a red scheduled run emails the last committer and is easy to miss for days, which is the exact failure mode being fixed. Hence Alertmanager. If reviewers prefer consistency with ci(lockfile): add hourly master lockfile-overrides drift monitor (BLO-24150) #1340 over reaching a human, that is the trade-off to argue.
Note Alertmanager's
repeat_intervalis 1h, so a persistent drift re-notifies hourly. Deliberate for a lapsed production gate, but it is a monitoring-side tuning knob, not something this PR sets.Not in scope: restoring
can_admins_bypass: falseand removingkkrooneeds the repo-admin credential (the App gets403on environment writes) and is an owner call while a deploy sits parked inwaiting. Tracked on the issue.On
docker.ymlalso hard-asserting the shape beforehelm upgrade(the open question BLO-22329 asked me to decide): recommendation is assert-and-report, never assert-and-fail, and in a separate PR. The 2026-08-08 widening was a legitimate, disclosed incident response to a company-wide dispatch deadlock; a hard-fail assert would have blocked it, and a control that blocks incident response gets disabled under pressure — strictly worse than not having it. It is not zero-value though: withcan_admins_bypass: truean admin can self-approve, and only a deploy-time check catches that at the moment it matters.Model Used
Claude Opus 5 (
claude-opus-5), 1M context window, extended thinking, with tool use and code execution.Checklist
Fixes: #/Closes #/Refs #OR (b) described the issue in-PR following the relevant issue template