Uh oh!
There was an error while loading. Please reload this page.
fix(standards-sync): a PR refusal stops the writes, not the audit (backend#2690) - #368
fix(standards-sync): a PR refusal stops the writes, not the audit (backend#2690)#368LukasWodka wants to merge 2 commits into
Conversation
…ckend#2690)
`remediate()` raising `AuthorUnusable` used to `break` the target loop, so a
credential that GitHub stopped taking on repo 1 of 16 left the report covering
a PREFIX of the fleet -- while the summary line went on printing
`{len(targets)} targets` and the drift counts described only the repos it had
reached. A mechanism reporting a result it never established.
Stopping the WRITES is correct and stays (#348: the same credential opens every
one of these, so carrying on would push a branch to all of them and open a PR
on none). Stopping the AUDIT was never correct -- classification is a read that
needs no credential, and `standards-sync.yml` argues exactly this twenty lines
above the secret: aborting before the audit "would turn 'PRs could not be
opened' into 'fleet state unknown' -- strictly less information". The
`author_refusal` path already obeyed that. This path contradicted it.
So the refusal now sets `remediating = False` and carries on classifying: every
target gets a row, repos after the halt read NOT REMEDIATED, and the footer says
which of the two things happened -- remediation halted at `beta`, audit complete
for all N.
Six selftest checks (60 -> 67), and two mutations rather than one: the properties
"the audit continues" and "no further branch is pushed" are independent, and a
single mutation cannot prove both.
Closestracebloc/backend#2690
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>LukasWodka
commented
Aug 27, 2026
|
| PR | closes | ticket framing |
|---|---|---|
| #366 | backend#2735 | Bugbot High, blocking the prod hop (.github#363, staging → main) |
| #368 | backend#2690 | Bugbot Medium/Low, shipped at the staging hop |
backend#2690 and backend#2735 are the same Bugbot finding recorded at two hops of the same promotion chain, so the two tickets are duplicates of each other as well.
Both PRs touch the identical three files and make the identical change:
break→remediating = False+continuein theAuthorUnusablehandler- rename the tracking variable (
writes_stopped_athere,halted_atin fix(standards-sync): a PR refusal stops the writes, not the audit (backend#2690) #368) - rewrite the
**ABORTED at …**footer - add selftest coverage and a mutation entry
They will conflict. Whichever merges second gets a conflict in standards-sync.py and in both test files.
Recommendation
Merge one, close the other, and close the losing ticket as a duplicate. I have no strong preference on which — they are equivalent in behaviour. Marginal differences:
- fix(standards-sync): a PR refusal stops the writes, not the audit (backend#2690) #368 carries a slightly larger mutation addition.
- fix(standards-sync): a create failure disarms the writes, it does not truncate the audit (backend#2735) #366 carries a row-scoped cause assertion that Bugbot forced here (a whole-report search was passing vacuously), plus the report wording fix for the
NOT REMEDIATEDrows, which otherwise still name the pre-flight cause after a create failure.
I am not closing either — the org rule is that an AI session does not close PRs without an explicit instruction, and this one is LukasWodka's on both sides. @LukasWodka / @saadqbal, one call closes it out.
Uh oh!
There was an error while loading. Please reload this page.
…nd#2690)
Bugbot on .github#368. The footer this change adds contains the words
"NOT REMEDIATED", so searching the whole report was satisfied before any
row was consulted -- gamma could be a bare drifted classification and the
check still passed. The missing-repo check beside it already matches
`| {r} |` per target; this one now does too.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>LukasWodka
commented
Aug 27, 2026
bugbot run |
There was a problem hiding this comment.
✅ Bugbot reviewed your changes and found no new issues!
Comment @cursor review or bugbot run to trigger another review on this PR
Reviewed by Cursor Bugbot for commit 128956e. Configure here.
Summary
remediate()raisingAuthorUnusableused tobreakthe target loop. A credential GitHub stopped taking on repo 1 of 16 therefore left the report covering a prefix of the fleet — while the summary line went on printing{len(targets)} targetsand the drift counts described only the repos the sweep had reached. A mechanism reporting a result it never established.Stopping the writes is correct and stays. #348's reasoning holds: the same credential opens every one of these, so carrying on remediating would push a branch to all of them and open a PR on none.
Stopping the audit was never correct. Classification is a read that needs no credential, and
standards-sync.ymlargues exactly this twenty lines above the secret — aborting before the audit "would turn 'PRs could not be opened' into 'fleet state unknown' — strictly less information". Theauthor_refusalpath already obeyed that; this path contradicted it.So the refusal now sets
remediating = Falseand carries on classifying:NOT REMEDIATED, and say which of the two causes applies (refused up-front vs. cannot open PRs mid-run);REMEDIATION HALTED at betaand that the audit covers all N targets, so "halted" and "audited" can be told apart.Test plan
scripts/tests/standards-sync-selftest.py— 60 → 67 checks, 0 failed._run_maingainedtargets=/remediate=parameters because this is a multi-repo property: a one-repo fleet has no "rest of the fleet" for the abort to have eaten.New checks assert the finding per repo (not by counting rows, so a report that lost
gammacannot pass by gaining a footer line), thatgammais never attempted, the footer wording, exit 2, and non-vacuity (an unrefused three-repo fleet still gets three PRs and is green).scripts/tests/standards-sync-mutations.py— 13 mutations, 0 stale, 0 malformed, 0 uncaught. Two new ones, not one, because the two properties are independent and a single mutation cannot prove both:breakmain: a mid-fleet PR refusal still audits EVERY targetmain: the refusal disarms remediation for the REST of the fleetBoth mutations edit the real
scripts/standards-sync.pyand re-run the real suite — no rule is re-implemented in the harness.Identity of the two cards
Verified rather than assumed — they are the same finding, not two:
backend#2690backend#2735.github#341).github#363)scripts/standards-sync.pyL651-665scripts/standards-sync.pyL651-665breakonAuthorUnusableaborts the remaining auditbreakonAuthorUnusableaborts the remaining classificationThe High-hop description names the same asymmetry this PR removes: "The identity-refusal path in the same function already disarms writes and keeps the read-only audit; this path stops both."
Closes tracebloc/backend#2690
Closes tracebloc/backend#2735
🤖 Generated with Claude Code
Note
Medium Risk
Touches org-wide standards-sync remediation and reporting in CI; behavior change is intentional (complete audit on credential failure) while preserving the no-half-rollout write gate.
Overview
When
--create-prsremediation hitsAuthorUnusable(PAT resolves but GitHub refusespr create), the sync used tobreakout of the target loop. That stopped classification for the rest of the fleet while the summary still claimed alllen(targets)repos were audited—a partial sweep dressed as a full one.Remediation still stops fleet-wide (#348):
remediatingis turned off and no further branches are pushed after the failure repo. The change is that the read-only audit keeps going—same principle as the upfrontauthor_refusalpath and whatstandards-sync.ymlargues for.Reporting is tightened:
halted_atreplacesaborted_after; drifted rows after a halt sayNOT REMEDIATEDwith whether the cause was upfront refusal vs mid-run PR failure; the footer usesREMEDIATION HALTEDand states the audit still covers all targets.Tests extend
_run_mainfor multi-repo fleets and add two mutation cases so “audit continues” and “remediation disarms” stay independently pinned.Reviewed by Cursor Bugbot for commit 128956e. Bugbot is set up for automated code reviews on this repo. Configure here.