Skip to content

release-train: develop -> staging - #450

Open
LukasWodka wants to merge 3 commits into
stagingfrom
release-train/to-staging
Open

release-train: develop -> staging#450
LukasWodka wants to merge 3 commits into
stagingfrom
release-train/to-staging

Conversation

@LukasWodka

@LukasWodka LukasWodka commented Sep 9, 2026

Copy link
Copy Markdown
Contributor

Automated promotion by the release train (RFC-0008 D14). Head is the train-managed release-train/to-staging branch (a mirror of develop), so it never collides with a human PR. Merged only when the fr-gate is green.


Note

Medium Risk
Changes org-wide CI auth, kanban card moves on deploy pushes, and how conflict/PR-title gates signal on PRs—high impact if token scopes or check-run PATCH behavior regress, but behavior is heavily self-tested.

Overview
Fixes private-repo kanban advancement by minting the release-train App token before PR extraction and calling GET /repos/.../commits/{sha}/pulls with that token (contents: read + pull-requests: read), instead of github.token, which 403’d on private repos and left cards behind shipped code. extract-advanced-prs.sh now surfaces real gh stderr on API failures so 403s are not misread as rate limits.

Conflict gate switches from commit statuses (scope the App cannot mint) to check runs (checks: write): reads latest state via REST check-runs, PATCHes the existing run when the verdict changes (avoid duplicate runs on the rollup), and keeps write-only-when-changed dedup.

Closing-ref gate treats zero-padded conventional-commit scopes like chore(0071) as RFC document numbers, not GitHub issue #71 (SCOPE_BARE_RE requires a non-zero leading digit), fixing false failures on rfcs adoption PRs that truthfully Part of an epic.

Reviewed by Cursor Bugbot for commit 20f7325. Bugbot is set up for automated code reviews on this repo. Configure here.

…ket (backend#3357) (#445)

The `closing-ref` gate read the conventional-commit scope `chore(0071)` as
GitHub ticket #71 and demanded the PR reference it. But a GitHub issue number is
never zero-padded -- `0071` is the rfcs repo's RFC-document convention
(measured 2026-09-08: `chore(0071)`, `docs(0077)`, `chore(0081)`, `docs(0075)`,
`docs(0067)`, each naming an RFC, none a ticket).

rfcs#78 (`chore(0071): Adopted -- epic tracebloc/backend#3289`) was the measured
casualty: `0071` read as #71, so the gate rejected the PR's truthful
`Part of tracebloc/backend#3289` -- which names the epic, not #71 -- and offered
only a FALSE `Closes #71` (the wrong issue) or deleting `(0071)` from the title
(the RFC traceability). That is rule 4's failure mode: a gate whose only remedy
is a lie or a deletion.

Narrow `SCOPE_BARE_RE` to require a non-zero leading digit (`[1-9]\d*`). An
unpadded `fix(71)`/`fix(#71)` still parses, so the narrowing costs no real
ticket any recall; a repo that means issue #71 never writes `fix(0071)`. The
`rfc-0068`/`rfc-664` scopes the same repo also writes never reached the parser
(a dash is no `#?\d+`), so this closes the one shape that did.

Selftest: adds the rfcs#78 end-to-end case (must PASS), the padded-scope parse
cases, and pins that an unpadded `fix(71)` with no reference still FAILS.
Mutation harness: updates the `#N`-scope anchor and adds one reverting the
narrowing, which the rfcs#78 case catches.

Part of tracebloc/backend#3357

Co-authored-by: Syed Saqlain <syedsaqlain@MacBook-Pro.local>
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
@LukasWodka

Copy link
Copy Markdown
Contributor Author

bugbot run

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Stale Bugbot comment from a previous run.

@tracebloc-release-train tracebloc-release-train Bot added the gate-nudge Toggled by the release train to (re-)fire the fr-gate label Sep 9, 2026
saqlainsyed007 and others added 2 commits September 9, 2026 11:56
…te can actually mint its token (backend#3242) (#446)

* fix(conflict-gate): write a check run, not a commit status (backend#3242)

The Conflict gate has never once run. Its token mint asks the
tracebloc-release-train App for `permission-statuses: write`, but the
App's installation does not grant `statuses` at all, so the mint is
refused ("The permissions requested are not granted to this
installation") before the sweep starts -- no successful run since it
landed.

The signal has to be written from OUTSIDE the PR onto its head sha
(a conflicted PR has no merge ref, so no `pull_request` job can run);
both a commit status and a check run satisfy that. The choice is a
permission one, and `statuses` is the scope the App cannot give. So
switch the two network seams from the Statuses API to the Checks API:
`checks: write` is a scope the App already holds. Same red-row-on-the-PR
signal, a scope the mint can actually get -- the same class fix as
backend#3360 (bugbot-gate reads off checkSuites) and backend#3222
(release-train reads the verdict off check-runs): never depend on the
`statuses` scope.

`classify`/`plan`/`STATE_FOR`/`CONTEXT` and the seam signatures are
unchanged; only `existing_state` (now reads
`commits/{sha}/check-runs`, newest run by id) and `post_status` (now
POSTs `check-runs` with a status/conclusion mapping) change API. The
check-run NAME is the same `conflict-gate / mergeable` context, so
nothing that might later require it needs renaming. Nothing else reads
the old commit-status context (only this gate's own tests did).

Selftest and mutation harness updated to the check-runs read/write and
the `checks: write` mint; a mutation pins that the read never returns
to a commit-status source and that the mint never re-adds `statuses`.

Closes tracebloc/backend#3242

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* fix(conflict-gate): update the one check run in place, not append a new one (backend#3242)

Bugbot (High): post_status always POSTed a fresh check run. Check runs APPEND
(unlike a commit status, one slot per context that success overwrote), and
statusCheckRollup -- what branch protection reads -- is worst-of across every
same-name run on the sha. So a later success could never clear an earlier
failure, and an in_progress (pending) POST left uncompleted kept the rollup
pending forever after mergeability was known.

post_status now PATCHes the greatest-id run we already own (via the shared
_latest_own_run helper, factored out of existing_state) and POSTs only when none
exists, so the single run transitions success<->failure<->in_progress in one
slot. New selftest case 13c exercises the real post_status: PATCH when a run
exists, POST when absent -- reddens if either reverts to always-POST.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* test(conflict-gate): retarget the two mutations my refactor moved (backend#3242)

Bugbot round 2 (two Medium): moving the name filter and the read-fail handler
from existing_state into _latest_own_run left two mutations stale/broken.

- The name-filter mutation looked for a standalone `if run.get(\"name\") ==
  CONTEXT:`; it now lives inside a list comprehension, so the anchor matched 0
  times and the property went untested. Retargeted to the comprehension filter.
- The read-fail mutation returned \"success\" from a function that must return a
  run dict or None; a bare string AttributeErrors in check_run_state and scores
  UNCAUGHT (harness broke). It now returns a completed-success run dict, so
  existing_state reads \"success\" instead of None and the mandatory-write property
  reddens as intended.

Harness: 29 mutations, 0 stale, 0 uncaught. Selftest: 124 assertions pass.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* fix(conflict-gate): force the check-runs read to GET, read it once, pin the method (backend#3242)

Address @saadqbal review on #446:
- _latest_own_run passed -f without --method GET, so gh api POSTed the read;
  there is no POST route on /commits/{sha}/check-runs, so it 404d on every
  call -- _latest_own_run returned None always, killing the dedup and forcing
  post_status down the CREATE branch forever (check-runs accumulate). Force
  --method GET and add filter=latest so the endpoint returns the current run
  per name (no reliance on paging a 30-per-page history).
- Read our run ONCE in sweep_repo and thread it into post_status, dropping
  the redundant second GET for changed verdicts.
- Document the pending-PATCH reliance (status->in_progress un-completes the
  run, so its old conclusion no longer contributes to the rollup) as a stated,
  re-confirm-before-arming assumption rather than a silent guess.
- Selftest now asserts any api read carrying -f also carries --method GET, and
  a new mutation strips --method GET; retarget the two mutations the refactor
  moved.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

---------

Co-authored-by: Syed Saqlain <syedsaqlain@MacBook-Pro.local>
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
…n, not the restricted workflow token (backend#3447) (#449)

* fix(advance-deploy-env): read /commits/{sha}/pulls under the App token, not the restricted workflow token (backend#3447)

Since .github#438 reached main (2026-09-08 19:10 UTC) the extract step read each
commit's PR with github.token. The org default workflow permission is the
restricted read (contents + packages only), and that endpoint needs
pull-requests:read on a private repo -- so every private-repo board advance
403'd and failed closed, while every public-repo one passed. Mint the
installation token (which already carries pull-requests:read) before the
extract step and run the read under it; keep the read's stderr in the
::error line so the next failure names its status.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* fix(extract-advanced-prs): build the stderr excerpt without a pipe, so pipefail cannot read head's early close as a failure

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* fix(advance-deploy-env): grant the mint contents:read — the extract step's call site moved onto this token (backend#3447)

Bugbot High on #449. The extract step now reads
`GET /repos/{r}/commits/{sha}/pulls` under the App token, which this mint
scoped to `pull-requests: read` only. `fr-gate.yml` records that endpoint as
needing `contents: read + pull-requests: read`, derived from its own calls
under backend#2157 and green as a required check ever since; no workflow here
reads it under `pull-requests: read` alone. Short by that one scope, this PR
reproduces backend#3447 in the same silent shape — the read 403s, the
fail-closed branch refuses the subject fallback, no card moves on any private
repo.

saadqbal's .github#324 finding is untouched and still correct on its own test
(check the call site, not the prose): the mapper call at `:94` runs BEFORE this
mint under `github.token`, so the router's need genuinely does not generalise
to it. What changed is that .github#438 added a SECOND consumer and this PR
moves it AFTER the mint — a call site that did not exist when #324 was written.
The comment block now says that, so the next reader does not re-derive the
dropped-by-analogy argument against a call it never covered.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
@LukasWodka

Copy link
Copy Markdown
Contributor Author

bugbot run

@github-advanced-security

Copy link
Copy Markdown

You are seeing this message because GitHub Code Scanning has recently been set up for this repository, or this pull request contains the workflow file for the Code Scanning tool.

What Enabling Code Scanning Means:

  • The 'Security' tab will display more code scanning analysis results (e.g., for the default branch).
  • Depending on your configuration and choice of analysis tool, future pull requests will be annotated with code scanning analysis results.
  • You will be able to see the analysis results for the pull request's branch on this overview once the scans have completed and the checks have passed.

For more information about GitHub Code Scanning, check out the documentation.

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 20f7325. Configure here.

Comment thread scripts/conflict-gate.py
listing = CD.gh_json(["api", "--method", "GET",
f"repos/{org}/{name}/commits/{sha}/check-runs",
"-f", f"check_name={CONTEXT}",
"-f", "filter=latest"])

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Latest filter hides pending check runs

High Severity

_latest_own_run lists check runs with filter=latest, which GitHub filters by completed_at and therefore omits in_progress runs. An undetermined sweep POSTs a pending run; the next sweep cannot see it, treats the head as empty, and POSTs a sibling success or failure. statusCheckRollup is worst-of across every same-name run, so the leftover in_progress check stays pending and can never be cleared — the stuck shape this rewrite’s PATCH path exists to prevent.

Additional Locations (1)
Fix in Cursor Fix in Web

Triggered by project rule: Bugbot guide — tracebloc/.github

Reviewed by Cursor Bugbot for commit 20f7325. Configure here.

@tracebloc-release-train tracebloc-release-train Bot removed the gate-nudge Toggled by the release train to (re-)fire the fr-gate label Sep 9, 2026
@LukasWodka

Copy link
Copy Markdown
Contributor Author

REFUSED. This promotion was blocked by 1 HIGH severity finding(s): Latest filter hides pending check runs, per the severity policy in release-train's README (High stops the line; an unreadable severity blocks too, because unknown is not low; Medium/Low are recorded and ship, at both hops).

Blocking threads stay UNRESOLVED by design, so this promotion remains blocked until they are fixed on develop. This is a second look at once-reviewed code -- it passed per-feature review on the source branch, and has NOT had functional review yet (that happens on staging).

What the train did with each:

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.

3 participants