Uh oh!
There was an error while loading. Please reload this page.
release-train: staging -> main - #318
Conversation
…lands last (#293) * ci(1979): arm the stale sweep — this repo joins the wave, and the flip lands last The final PR of the backend#1979 wave, and the last exit-criterion work for backend#1680. Two things, in one PR because they cannot be separated: 1. THIS REPO GETS ITS CALLER. It was deliberately left out of .github#288 (Bugbot): `exempt` plus a caller on the audited branch is itself a finding (caller-drift.py:2260), so shipping the caller with the reusable would have gone green on a repo the audit was told to skip. 2. ALL 17 MIGRATION ENTRIES FLIP `exempt` -> `required`, and the `stale_backlog_migration_in_flight` anchor is DELETED rather than left behind. Its own text says so: "if this anchor is still here after that, it has become the same finding as wip_limit_check_has_no_callers -- a reusable that shipped and was never wired up, with a written excuse." `release-train` and `rfcs` keep their exemptions untouched, because those rest on different reasons that are still true: release-train because enrolling the repo that drives promotions into an unattended closing sweep is a decision somebody should take deliberately (still UNDECIDED, and the note says so), and rfcs because it holds decision documents rather than a backlog -- an RFC nobody has acted on is a decision nobody has taken. ORDERING, which is the whole reason this is a separate PR: flipping to `required` before a repo caller lands would redden the org audit for every repo still waiting -- the drift window made to look permanent, which is how a tier teaches people to ignore it. So this merges LAST, after the 16 caller PRs. Verified: 17 entries required and 2 deliberately exempt (asserted by reading the parsed inventory, not by counting the diff), the spent anchor at zero occurrences, `make check` green (41/41 house-rules cases), and caller-drift-selftest 197/0. backend#1979 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * ci(1979): move .github's own caller to its own PR, so this one can go green Bundling the caller with the flip deadlocks: the audit reads caller presence from the audited branch via the API, so `.github`'s caller is absent from `develop` until it merges, and `gate` is a required context. The caller now lands in .github#307; this PR is purely the 17-entry flip. Refs backend#1979 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
…he fleet again (backend#2284) (#312) Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
…#2348) (#313) * feat(2348): a bug-labelled issue lands in Ready, not Backlog (backend#2348) The org rule -- "bugs get `work-type:bug` and go straight into `Ready` (defects skip refinement)" -- was written in org-standards.md and implemented nowhere: add-to-kanban puts every issue at `Backlog` and no workflow read the label. Measured 2026-08-22: seven bug-labelled tickets in one day, all seven left in `Backlog`. Implemented as a job on `customer-priority-bump.yml`, the only reusable already triggered by `issues: labeled` and already called by 16 of 19 repos -- so it reaches the fleet with no per-repo edit and touches none of conformance-gate's GUARDED contract files. MONOTONIC: promotes only from `Backlog` or from no Status, and asks the BOARD for the direction (col_index byte-identical to the closure router's) rather than holding a rank table. An inverted Backlog/Ready order refuses rather than performing a demotion. FAILS CLOSED like advance-deploy-env, not like the closure router: an unreadable board, a truncated page, an unplaceable column or a card that never reaches the project are all RED. Declining quietly here would reproduce the very defect being fixed -- and a `labeled` event fires once, where a cron gets another go next week. Tests: 46 assertions, extracted from the YAML by `# selftest:` markers and run verbatim; the Status vocabulary is derived from advance-deploy-env's rank() and cross-checked against the sibling suite, so all twelve columns are driven. 21 mutations, 0 stale, 0 uncaught. `make check` green. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix(2348): the Status write goes through the same errors[] rejection as the reads (backend#2348) Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
The develop-first rule named `docs` as its sole exception. `rfcs` has the same shape and the same reason -- it publishes no artifact, has no deploy stages, and is not on the release train -- so a promotion gate there has nothing to gate. Decided on backend#2347. This line was the last place still asserting otherwise. `repo-inventory.yml` has described `rfcs` as a single-branch repo all along: `release_train: false`, so `audit_branch: develop-first-on-train` already resolves to the default branch, and the `develop` role carries `exempt: *no_develop_branch_single_branch_repo` -- "single-branch repo: `main` is the only integration branch, so there is no `develop` to protect. Structural, not a gap." So the machine-readable side needs no change; the prose was the drift. Stated as a class with its members named, and pointed at the inventory, because a bare list drifts and a bare class invites argument. Why nobody caught it: the conformance guard flags a stale exemption only when the role's branch appears AND carries protection. `rfcs/develop` existed and was completely unprotected, so the contradiction between "there is no develop" and a live develop branch was invisible by construction. rfcs#45 deletes that branch and makes reality match what this file and the inventory both now say. Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
…scribing two repos (#306) * fix(2242): say why rfcs is exempt, accurately, and stop one anchor describing two repos The `rfcs` block asserted "this repo has no `develop`" in two places. It has one, and it takes merges. Corrected both, without touching the `required` flip that is still blocked on the callers reaching rfcs' default branch. Refs backend#2242, tracebloc/rfcs#41 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix(2242): name the surviving precedent, not the anchor #293 deleted (backend#2242) Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
release-train: develop -> staging
LukasWodka
commented
Aug 24, 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 cff3b74. Configure here.
…its caller (#320) The conformance audit went red and blocked the .github prod promotion (#318): release-train: customer-priority-bump.yml is marked `exempt` but a caller exists (customer-priority-bump.yml). The exemption is stale - promote it to `required` or delete the caller. backend#2396 wired that caller in release-train this morning (c0c6cf7, 'so a bug lands in Ready'); this file still claimed the repo had none. The audit is right and the inventory was wrong. Flipped release-train's entry to `required`. The shared anchor STAYS: it is still cited by two other repos that genuinely have no caller, so deleting it would be wrong -- this is the opposite of the kanban_closure_router case documented a few lines below, where the anchor is cited once and a flip orphans it. Also corrected the citation count in that neighbouring comment from 3x to 2x, and recorded why the anchor survives. That count is the only thing telling the next person whether a flip orphans the anchor, and nothing checks it -- no check flags an orphaned YAML anchor, as the same comment says. A stale count there is how the next flip deletes a live anchor or keeps a dead one. Sequencing is per this file's own note: land the caller first, flip the entry after. The caller is already on release-train's develop. Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
…#314) * ci(2364): a PR whose title names a ticket must link it (backend#2364) Merging a fix never closed or advanced its ticket. `closingIssuesReferences` was 0 on 7 of 7 sampled merged PRs (release-train#109/#108, .github#304/#300, backend#2266, client#774, docs#131). The house convention puts the ticket in the PR TITLE; GitHub creates a closing link ONLY from a keyword in the BODY, so a title reference is inert. `kanban-closure-router.yml` fires, finds no linked issue, and correctly does nothing -- every kanban workflow green, every card unmoved. Adds a `closing-ref` job to the EXISTING `set-pr-status.yml` reusable: parse the real title, assert the real `closingIssuesReferences` contains what it names. Derived, not restated (rule 1): two live reads, no list of tickets, repos or authors. The four title forms are measured, not imagined. A bare `#N` outside parentheses is deliberately NOT read as a ticket -- backend#2309's `#2271` is prose about a PR, and scanning loose `#N` would redden a compliant PR. Fails closed (rule 3): a blank title, a GraphQL error, `pullRequest: null`, a null/ownerless node, or `totalCount > len(nodes)` all exit 2 as "cannot tell", never a pass and never a finding against the author. The truncation test is load-bearing beyond pagination -- a link to an issue the token cannot read comes back missing from `nodes` while `totalCount` still counts it, which is indistinguishable from "not linked". The cross-repo trap is its own verdict: `WRONG_REPO` is reported apart from `MISSING` because the remedies differ -- a bare `Closes#304` in `.github` links `.github#304`, closing the wrong issue on merge, and needs the line rewritten rather than added. Fixtures are measured bytes (the backend#2114 lesson), captured with `gh api graphql` and re-verified against the live API before commit. Tests: 102 selftest assertions; 34 mutations, 0 stale, 0 uncaught. The mutation harness edits the real gate and re-runs the real suite -- no inline copy of any rule (rule 9, .github#114/#115). Every anchor must match exactly once, which is the assertion that it actually applied. Refusals are asserted by their own message, never a catch-all (rule 10). The commit-type vocabulary is derived out of org-standards.md and the derivation fails closed if it finds nothing (rule 6). Arming: `closing-ref` is a required status check NOWHERE -- measured across 19 repos x develop/staging/main/master x both classic protection and rulesets -- so a finding blocks no merge (rule 4). Callers trigger on opened/reopened/ready_for_review/converted_to_draft, not `synchronize`, so the 13 open PRs that would report a finding are not reddened by a push. Touches no file in `conformance-gate.yml`'s GUARDED list, and needs no `repo-inventory.yml` row: the inventory tracks callers, one row per reusable, and `set-pr-status.yml` already has its rows. Closestracebloc/backend#2364 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix(2364): the remedy stops guessing a repo it cannot know (backend#2364) Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix(2364): the scope pattern admits a leading dot too, so .github stops depending on a coincidence (backend#2364) Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
…restating it (backend#2422) (#321) The bullet named its own exceptions -- "`docs` and `rfcs` are main-only" -- one sentence after naming `repo-inventory.yml` as the authority and saying "this line follows it, not the reverse." It then drifted from that authority on BOTH examples: * `docs` has been on the release train since 2026-08-04 (release_train: true, develop: required, staging present) while this line called it main-only. * `rfcs` had a `develop` taking merges, measured 2026-08-22 in backend#2242 / .github#306, while this line called it main-only. That is the restated-rule defect the file's own meta-rule warns about, and it is not theoretical: I read this line on 2026-08-19, concluded `rfcs` should be main-only, created a `develop` there to satisfy the first bullet, and forked the repo -- 9 commits on develop against 5 on main over four days, both sides editing README.md, reconciled by hand in rfcs#50. So the enumeration is removed rather than corrected. A list of repo names in prose will drift again; a pointer to the file that is measured will not. Also records the trap the inventory found and no check catches: a `develop` created on a non-train repo and left unprotected is invisible to the guards (`develop_unprotected_non_train` -- "a `develop` created and left UNPROTECTED is not flagged ... no check was going to surface it"). Creating one to satisfy the train bullet forks the repo silently. Stating it here because the guard cannot, which is exactly what this file is for. Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
…f the flip (backend#2396) (#316) Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
…ey emptied (#319) * fix(2396): release-train rides the label trigger, so its exemption stops being stale (backend#2396) Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * ci(2396): flip .github and rfcs to required, and delete the anchor they emptied Both callers are live on their default branches (.github#316, rfcs#47), so the `exempt` rows became the stale-exemption finding caller-drift.py:2309 exists to catch. Flipping them takes the anchor to zero citations, so its definition is deleted in the same PR rather than left as dead prose. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
release-train: develop -> staging
…p commit (backend#2365) (#323) * fix(scripts): branch ownership comes from the PR author, never the tip commit (backend#2365) A "my branches" list built from `%(authorname)` on the ref answers "who touched it last", not "whose work is this". Pushing a review fixup onto someone else's branch rewrites the tip author and changes nothing about who owns it -- and the next command in that workflow is `push origin --delete`. Measured: `client` PRs #395 and #592 are @shujaatTracebloc's and both showed Lukas as tip author. backend#2001's per-person table is NOT affected -- checked first, and it attributed by PR author already (`inventory.py`: "merged/closed PR author wins, else the tip's git identity"). The defect is that the rule lived in a throwaway script, so every later query re-derives it and the obvious derivation is wrong. `branch_owner.py` is the one place that answers it: PR author first (exact head oid, then head name), the oldest commit not on the default branch for a branch with no PR, and `unattributable` otherwise. `attribute()` takes no tip-author parameter, so no path can return one. Fails closed -- an unread or truncated PR list, an ambiguous head name and a deleted PR author all refuse rather than guess -- and `origin/HEAD` is verified against the remote before the first-commit signal is used, because a stale default corrupts that signal exactly. `CreateEvent` is not used for the no-PR case despite the ticket proposing it: /repos/{repo}/events caps at 300 events, which on backend spans under four hours and holds two branch creations. 71 assertions and 26 mutations, wired into the already-required `selftests` context; three mutations were uncaught on the first run and the tests were strengthened rather than the mutations dropped. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(scripts): a withheld check is not a finding, and an absent tip is not a mismatch (backend#2365) Both of Bugbot's findings on #323, and both are the same shape as the defect already fixed in the first commit: a message asserting a comparison that never ran. Reproduced by construction before changing anything. 1. The multi-author reuse refusal ended "and none is at the current tip" even with no tip in hand -- reachable for a branch named on the command line that this clone has not fetched, whose head name two people have used. The single -author `pr` arm already split those two sentences; this arm now does too. 2. When the default branch could not be confirmed, `main` withheld the first-commit signal by passing "" and `attribute` then reported "no commit on this branch that is not already on the default branch" -- a fact about the branch, from a check nobody ran. Appending the reason did not retract it. `attribute` takes `first_commit_problem` now, so a withheld signal is reported as unmeasured with its reason, and the measured-and-empty wording is pinned apart from it. 77 assertions, 29 mutations, 0 stale, 0 uncaught. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(scripts): a branch list that could not be read is not an empty clone (backend#2365) Bugbot's medium-severity finding on 9102520, and it is the sharpest one yet because it is the fail-closed rule turned on this module's own third seam. `remote_branches` collapsed a failed `git for-each-ref` into `[]` with no problem string, and `main` printed that as `0 branch(es)`, exit 0 -- a clean bill of health from a read that never happened, while the other two seams (`pull_requests`, `default_branch`) refuse explicitly. "I could not read the branch list" and "this clone has no branches" are different answers and only one of them is a fact about the clone. It returns `(refs, problem)` now and `main` refuses with exit 2, because without the ref list there is no tip for any branch and no way to tell whether a named one exists -- so every verdict below it would be weaker than it looked. A genuinely empty remote still reports no problem, pinned by its own case so the two cannot collapse back together. Bugbot's other comment on that head is stale: it restates the absent-tip finding already fixed in 9102520 and its own footer names commit 6f45cdc. Verified by construction on the current tree -- the refusal reads "and no tip was supplied to break the tie with". 80 assertions, 30 mutations, 0 stale, 0 uncaught. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(scripts): audit the other reads, and pin both empties apart at the seam and in main (backend#2365) Saqlain's requested change on #323 landed in 7a87b83, before his review arrived. This is the rest of what he asked for, and it found a fourth instance he had not seen. AUDITED ALL FOUR READS rather than fixing only the reported one. `pull_requests` and `default_branch` already refuse with a problem string. `remote_branches` was fixed in 7a87b83. `first_commit_author` had the SAME defect and worse: its condition was `if rc != 0 or not out`, conflating a FAILED `git log` with a branch that genuinely has no commits off the default branch, and `attribute` then reported the second one -- "no commit on this branch that is not already on the default branch" -- for both. A fact about the branch, from a read that never ran. It returns `(author, problem)` now, and `main` carries that reason through. THE TWO EMPTIES ARE PINNED APART IN BOTH DIRECTIONS, for both reads: collapse the failure into the success and the mutation reddens; report the genuine empty AS a failure and it reddens too. Asserting only "something empty came back" would have passed in a world where both refuse identically. AND `main` IS NOW TESTED, which is what made this worth doing. Dropping the one argument that carries a seam's problem into the rule reverted the whole fix while every isolated case stayed green -- the mutation run caught it as UNCAUGHT, so the entry point gets driven directly: a failed history read renders as unmeasured, a genuine empty renders as no-unique-commits, a failed enumeration exits 2 with no rows, and a genuinely empty remote exits 0. 92 assertions, 35 mutations, 0 stale, 0 uncaught. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix(scripts): a default branch the clone never fetched is one fact, not N failed ranges (backend#2365) Bugbot's `Failed first-commit read looks empty` names the seam already fixed in 6665d13, and two sub-cases with it. Both were verified by construction: an unfetched default branch and a CLI-named branch absent from the clone now report "the oldest-commit signal was not measured", naming the failed command, rather than "no commit not already on the default branch". The root cause behind the first one was still open, though. `default_branch` trusted `gh` for the NAME and never asked whether this clone has the ref, so a checkout that never fetched the remote's default produced one failed `default..branch` range per branch -- honest since 6665d13, but blaming N ranges for one missing ref, and unreadable on a repo with hundreds of branches. It is verified with `rev-parse --verify` now and reported once, as its own fact, with the fetch that fixes it. 95 assertions, 36 mutations, 0 stale, 0 uncaught. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
LukasWodka
commented
Aug 24, 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 2700b9e. Configure here.
…ured" (backend#2157) (#324) * fix(2157): scope the four kanban mints whose reason was "not yet measured" (backend#2157) Four of `mint-scope.py`'s ten exemptions said, in as many words, "scopes not yet measured". This measures them and drops the rows. Each scope set is derived from the workflow's OWN calls -- every `gh api`, `gh issue`, and GraphQL query/mutation the App token touches -- and from GitHub's documented per-endpoint requirement, not copied between files: advance-deploy-env issues:read pull-requests:read org-projects:write customer-priority-bump issues:write fr-pass-comment issues:write pull-requests:read org-projects:write kanban-closure-router issues:write pull-requests:read org-projects:write `contents:write` -- the grant that makes a leaked token able to push -- drops from all four, along with administration/actions/checks read. Two derivations are worth naming because they are the ones a reader would guess wrong: * labelling a PULL REQUEST (kanban-closure-router's sibling-merge marker) needs only `issues: write`. The label endpoints document "at least one of" Issues write / Pull requests write, and every shared action on a PR is served by the Issues endpoints. So pull-requests stays at READ. * the issue-comment REACTION endpoint (fr-pass-comment's thumbs) documents `Issues: write` ALONE -- no pull-requests alternative -- which is what forces a write grant in that file rather than a read one. `repositories:` is left unnarrowed everywhere: advance-deploy-env's closing-issue advancement and kanban-closure-router's closer lookup are deliberately cross-repo, and repo narrowing is measured in this org for a READ and not for a WRITE. Six exemptions remain, each for a stated reason rather than for lack of time, and the note above EXEMPT now says which is which. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix(2157): the board mappers read another repo over the API, so contents:read stays (backend#2157) Bugbot High, confirmed by saadqbal: the router mint dropped contents entirely while branch_status_map.py fetches the CALLER repo .kanban.yml with that token. The mapper refuses on any non-404 fetch failure and the call sites fall to the holding state, so a 403 would park every merged PR at override-unusable with a green job. advance-deploy-env.yml has the same three call sites and was flagged by nobody; scoping one and not the other would have shipped the same silent mis-routing through the other writer. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix(2157): drop the contents grant advance-deploy-env never uses (backend#2157) saadqbal on .github#324, and he is right. I added contents:read there by analogy with kanban-closure-router without checking the second instance. advance-deploy-env has ONE mapper call, at :94 in the env step, which runs BEFORE the mint and under github.token -- the comment at :169 already said so. It also maps $GITHUB_REPOSITORY, its own repo, where the router maps $REPO_FULL, the caller. Same script, different token, different repo: the scope is required there and inert here. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
…red (#325) `repo-inventory.yml` marked `advance-deploy-env.yml` and `kanban-closure-router.yml` as `exempt` under `rfcs`, while both callers exist on the branch the guard reads. `caller-drift` reports it as two stale-exemption findings, which fails `conformance-gate` -- a required check -- and blocks .github#318, the staging -> main promotion. Both exemptions were explicitly STAGED, not structural, with a written flip condition: "once rfcs#41 has reached rfcs' DEFAULT branch, which is `main`". That condition is met. rfcs#41 merged to `develop` 2026-08-23 and rfcs#45 reconciled it into `main` 2026-08-24, so both callers are on `main` -- which is the audited branch here, because `rfcs` is `release_train: false` and `audit_branch: develop-first-on-train` falls back to the default branch for a repo the train does not promote. The `kanban_closure_router_caller_missing` anchor is deleted in the same commit: this was its only citation, so the flip would otherwise leave it defined and uncited, and no check flags an orphaned YAML anchor. Both staging notes are deleted rather than reworded, per their own clause that a sequencing note outliving its landing "has stopped being a sequencing note and become a written excuse". No check logic changed -- caller-drift.py derives caller presence from each repo's `uses:` values, so this is data catching up to reality. Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
Their exemption reason was that branch protection can return LESS to a narrower token rather than erroring -- a 200 with a field missing, which a checker reads as "not configured" on a green run. Right worry, so it was measured instead of argued, and it landed on the other workflow. bricked-prs: protection does NOT degrade silently. Without administration:read it answers 403 and read_protection() reports an error. Narrowed to five READ scopes; bricked-prs.py over four repos gives output identical to the full grant. actions:read is required and non-obvious -- it is what lets GraphQL resolve statusCheckRollup -> commit.status on a private repo. merge-settings-drift: reads no branch protection at all. It reads GET /repos, and that endpoint IS the silent one -- the four merge-setting fields are returned only to a caller with PUSH access, 200 either way. contents:write is the one grant it measurably cannot lose; five others go. Every dropped permission was re-added one at a time to prove it load-bearing. Refs tracebloc/backend#2157 Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
…ckend#2449) (#329) Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
release-train: develop -> staging
LukasWodka
commented
Aug 25, 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 1212781. Configure here.
…h them (#332) * fix(2498): rfcs' two stale exemptions, and the reason that rotted with them `caller-drift` reported 2 findings on the prod mirror .github#318, both on `rfcs`, and both the same shape as the ones fixed on 2026-08-24: the fleet moved and `repo-inventory.yml` did not. * `code-quality.yml` was `exempt: *code_quality_caller_missing`, and the caller is NOT missing -- `code-quality-caller.yml` is on `rfcs/main`, which is the branch this guard reads for a `release_train: false` repo. Measured, because the branch decides the answer: it is NOT on `develop`, so checking the wrong ref reads the opposite way. * `protection.develop` was `exempt: *develop_unprotected_non_train`, whose text says `develop` "carries no branch protection". Measured today it matches the develop baseline on EVERY key -- classic_protection, min_reviews 1, bypass_reviews [], strict false, force-push and deletion blocks, conversation resolution, and exactly the three `quality / *` contexts -- so it is `required` with no `divergent` cell. `enforce_admins` is false and the baseline does not assert it on develop (backend#1276 D1), which is conformant rather than divergent. That second one is the anchor's own warning coming true. Its text says a `develop` created and left UNPROTECTED "is not flagged ... no check was going to surface it" -- and none did. The row went stale silently and only the promotion audit caught it. AND THE THIRD EDIT, which no finding asked for. The `prod` divergent cell drops the three `quality / *` contexts "BECAUSE the producer is absent". Flipping the caller row makes that sentence false, so it is corrected here rather than left behind a fixed field. The cell itself stays `[]` -- that IS the measured state, `rfcs/main` requires ZERO contexts -- so only the justification had rotted, which is exactly the failure backend#2449 added the citation guard for, one field over. Left as a named open question rather than decided quietly: now that the caller exists and reports those contexts on every `rfcs` PR, `main` COULD require them. That is a protection change, not an inventory correction. Verified: `caller-drift-selftest.py` 197 pass / 0 fail, and a live audit against the whole org now reports "No drift. Every repo read, every entry matched." Closes #2498 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix(2498): stop asserting a citation count that has now rotted twice Bugbot, and saadqbal reviewing .github#332. The `code_quality_caller_missing` anchor said "Down to THREE citations (claude-skills, release-train, rfcs)". Flipping `rfcs` to `required` leaves TWO -- claude-skills and release-train -- so the count and the named list are false the moment this PR merges. THE FIX IS TO DELETE THE TALLY, NOT TO DECREMENT IT. That sentence has now been wrong twice in the same way: it previously said THREE and named claude-skills/docs/release-train while FOUR entries pointed at the anchor, and it says THREE and names `rfcs` after this PR removes it. Its own last line already told the reader to trust `grep -n` over the sentence -- and the list outlived the advice. Nothing checks a prose count: `reason-citations.py` verifies that an issue cited in a reason is still OPEN, not that a tally is still right. So the roster is derived, and the grep that derives it is written down. The remediation history stays, because that is measurement rather than a tally -- model-zoo/start-training (backend#1420), docs, and now rfcs (backend#2498), each having gone stale the same way. A PR whose thesis is "stale prose rots silently" should not ship new stale prose in the file it is fixing, which is saadqbal's point and it is a fair one. AND THE OPEN QUESTION IS NOW A TICKET (backend#2501), not a comment. Naming it in this file is the same mechanism that just failed; a cited issue is at least checkable by `reason-citations.py`. The comment now points at it. Verified: caller-drift-selftest 197/0, reason-citations clean. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix(2498): anchor the derivation, which over-counted exactly what it replaced saadqbal and Bugbot on .github#332, and the defect is the one this hunk was written to remove. The anchor now tells a reader to derive the roster with grep -n "exempt: *code_quality_caller_missing" repo-inventory.yml and run verbatim that returns FOUR lines: two real aliases (claude-skills, release-train) and two COMMENTS that mention the anchor by name. So the prescribed derivation over-counts by exactly the two entries just removed from the sentence, and a reader following it lands back on "three or four" -- which is where this sentence started. Anchored to value position at line start, it returns 2: grep -nE '^\s*exempt: \*code_quality_caller_missing\s*$' repo-inventory.yml The class is worth naming, because saadqbal counts this as the fifth instance today of a check or instruction that greps source text and cannot tell code from commentary (client#808, e2e-test-agent#233 twice, tracebloc-engine#732, this). A derivation is only better than a tally if it derives the right set. Verified on this head: unanchored 4, anchored 2. Selftest 197/0, reason-citations clean. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix(2498): enter design-system-v2, the repo that appeared mid-review The `audit` failure on this PR was never about this PR's diff. `tracebloc/design-system-v2` was created 2026-08-25 11:35, WHILE this was in review, and `caller-drift` went red on design-system-v2: active in tracebloc but absent from repo-inventory.yml That is backend#2498's second half in one sentence: the audit reads the LIVE FLEET rather than the diff, so `.github` can only merge at an instant when every repo in the org is conformant -- and the org does not hold still. IT COULD NOT BE ENTERED WHEN IT FIRST APPEARED, which is why this took two attempts and one revert. While the repo was empty the guard refused to audit it at all ("An empty repo is a legitimate state, but it is not one this guard can audit"), so adding an entry only moved the finding from `absent from repo-inventory.yml` to `repo has no branches`. Neither is fixable from this file. The entry became possible only once the repo got its first branch -- the right order, since the inventory records measured reality and there was nothing to measure. Measured on `develop`, its DEFAULT and only branch: private, unprotected, no `staging` and no `main`, ten org callers present (version-bump-gate, advance-deploy-env, code-quality, customer-priority-bump, fr-gate, stale-backlog, fr-pass-comment, kanban-closure-router, set-pr-status, plus the add-to-kanban copy), the three unrolled-out ones absent, and both quality files present. Verified: live org audit now reports "No drift. Every repo read, every entry matched." Selftest 197/0, reason-citations clean. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
…y so (backend#2284) (#333) claude-skills#36 merged at 13:46, putting the first bugbot-gate caller anywhere in the org onto claude-skills/develop. That makes its exempt row stale (caller-drift.py:2309) and falsifies the anchor in the same move. The anchor claimed "bugbot-gate has ZERO callers anywhere in the org". True when written on 2026-08-23, false from 13:46 today -- the reason rotted at the moment the thing it described changed, which is the shape backend#2449 exists for. Its replacement derivation is anchored (backend#2507): the bare identifier also matches the paragraph describing it, so an unanchored grep would count its own documentation. Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
…quired (#335) * fix(inventory): claude-skills' code-quality caller, and the reason it falsifies REBASED ONTO develop AND NARROWED. This branch originally also flipped `claude-skills`' `bugbot-gate.yml` row and re-measured the `bugbot_gate_rollout_pending` anchor. .github#333 (backend#2284) landed both while this was open, and did the anchor better -- it names `claude-skills#36` as the first caller. Those edits are dropped rather than merged: two PRs asserting the same fact in different words is how the next reader learns to distrust both. What is left is the half #333 did not cover, confirmed by running the audit against current develop: 1 drift finding(s): - claude-skills: code-quality.yml is marked `exempt` but a caller exists So: 1. Flip that row to `required`. `code-quality-caller.yml` is present in `tracebloc/claude-skills/.github/workflows`. 2. Fix the reason the flip falsifies. `claude-skills` prod `required_checks` said the three `quality / *` contexts are dropped "BECAUSE the producer is absent", citing the very exemption being removed. The measured reason is narrower: `claude-skills/main` carries no required status checks at all (classic protection, empty `contexts`, measured 2026-08-25), because it is non-train infra whose `main` is a working branch. `required_checks: []` was and stays correct; only its cause was wrong. `release-train` carries the SAME wording and keeps it -- verified it has no code-quality caller, so its producer really is absent. One comment became false; the other did not. `caller-drift.py` against the live org: 1 finding before, "No drift. Every repo read, every entry matched." after. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix(inventory): the anchor's own counts, falsified by this PR's flip Bugbot Medium on .github#335, and correct. Removing the `claude-skills` alias made the `code_quality_caller_missing` anchor's own prose false: said: FOUR lines -- two real aliases and two comments measured: THREE lines -- ONE real alias (release-train) and two comments Both numbers re-measured and stated, plus the remediation history extended with `claude-skills` (2026-08-25, .github#335, caller added by claude-skills#36) -- the thread flagged that omission too. The anchor stays rather than being deleted: `release-train`'s citation is genuine, verified again here (no code-quality caller in its workflows). WHY THIS KEEPS RECURRING, since this is the third time in one PR that a flip falsified a sentence. A count written into prose is falsified by the NEXT flip, every time -- this anchor's text already carried that scar ("A reader following it lands back on 'three or four', which is where this sentence started") and acquired a fresh one anyway. The note now says so explicitly, so the next person flipping a row knows the count is theirs to re-measure. Not proposing a mechanism here. But a guard that parses the anchor's stated counts and compares them to the two greps would end the class, and it is the same shape as the citation guard .github#329 already landed. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix(inventory): claude-skills has no fr-gate caller, and cannot use one UNRELATED PRE-EXISTING DRIFT, surfaced mid-review and fixed here because it is what keeps `audit` (and therefore the required `gate`) red on this PR. Nothing in this branch touched the fr-gate row -- `git diff origin/develop..HEAD` does not mention it -- the audit simply started reporting: claude-skills: MISSING required caller for fr-gate.yml on develop. Measured: `fr-gate-caller.yml` is absent from `tracebloc/claude-skills`, while the inventory carried `fr-gate.yml: required`. So the row asserts a caller that is not there. FLIPPED TO EXEMPT RATHER THAN RESTORING THE CALLER, and the reason is in the file already: this entry's own `rulesets.promotion_merge_commit_only` exemption states that claude-skills has "no staging branch and no promotion PRs". The fr-gate only reports on a PR targeting staging/main/master AS A PROMOTION, so there is nothing here for it to gate -- structurally the same case as release-train's fr-gate exemption. Requiring a context nothing produces is the client#665 defect this file warns about two entries above. The remedy runs both ways and the reason says so: if the caller was removed by mistake, restore it and flip this back. The audit reports either direction. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
release-train: develop -> staging
LukasWodka
commented
Aug 25, 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 416da9b. Configure here.
Uh oh!
There was an error while loading. Please reload this page.
Automated promotion by the release train (RFC-0008 D14). Head is the train-managed
release-train/to-mainbranch (a mirror ofstaging), so it never collides with a human PR. Merged only when the fr-gate is green.Note
High Risk
Changes fleet CI authentication scopes, kanban board writes, and org conformance inventory—mis-scoped tokens or promotion logic can fail silently or block merges across many repos.
Overview
This promotion bundles org-wide GitHub Actions hardening and kanban automation fixes in
tracebloc/.github.Kanban triage: The reusable
customer-priority-bump.ymlgains abug-to-readyjob so addingwork-type:bugpromotes a card fromBacklog→Ready(with monotonic board checks, GraphQL fail-closed behavior, and retries foradd-to-kanbanraces). A thincustomer-priority-bump-caller.ymlwires the trigger for this repo; docs note three repos still lack the caller.PR linking:
set-pr-status.ymladds a separateclosing-refjob runningclosing-ref-gate.py, which flags PRs whose title names a ticket but whose body does not link it via closing keywords (fixing silent kanban closure misses). The PR template checklist is updated to match.Credentials: Several workflows now mint narrow
tracebloc-release-trainApp tokens (documented per call site), includingkanban-closure-routerkeepingcontents: readfor cross-repo.kanban.ymlfetches.merge-settings-driftandbricked-prsscopes were measured rather than guessed.Governance & inventory: CI runs
make reason-citationsagainst liverepo-inventory.ymlticket citations;repo-inventory.ymlflips many callers torequired(e.g. stale-backlog, customer-priority-bump), addsbugbot-gaterollout exemptions,design-system-v2, and correctedrfcs/claude-skillsrows. Newbranch_owner.pydefines branch ownership by PR author, not tip commit.org-standards.mdpoints branch models at the inventory and documents the bug-label automation caveat.Reviewed by Cursor Bugbot for commit 416da9b. Bugbot is set up for automated code reviews on this repo. Configure here.