Uh oh!
There was an error while loading. Please reload this page.
promote: closure-router fix, CODEOWNERS, and actionlint to main - #70
Conversation
An issue closed by hand as completed was routed to Prod. The closing-PR lookup returns an empty base when no PR closed the issue, and the case statement's `*)` arm mapped that empty string to "Prod". This was an explicit default, not an accident of fallthrough, and it also swallowed the case where the GraphQL lookup simply failed. Observed live: backend#1171-#1176 were ticked off by hand on 2026-07-25 and all six were moved to Prod, despite their code having been merged only to develop. The run logs show "Routing decision: status=Prod" while closedByPullRequestsReferences returned an empty list for each. This matters beyond tidiness. fr-gate.yml is a required status check that lets a promotion PR merge only when every contained item sits in Ready for staging or Ready for prod. An item falsely parked in Prod satisfies that gate with nobody having reviewed it, so this bug can let unreviewed work through the very gate designed to stop it. Fix: separate "no closing PR" from "closing PR with an unrecognised base". With no closing PR the step now emits an empty status, and the update step (already guarded on a non-empty status) skips entirely, leaving the card in whatever column it was already in. Closing an issue by hand tells us nothing about where the code is deployed, so the honest answer is to change nothing. not_planned and a missing state_reason still route to Cancelled, and every PR-merge route is unchanged. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
fix(closure-router): a manual issue close must not invent a deploy state
`main` has had `require_code_owner_reviews: true` since branch protection was applied, but this repository has never contained a CODEOWNERS file — so the requirement resolved to nothing and any two of the seven org members could change the gates themselves with one approval and no CI. That matters more here than anywhere else. 96 caller workflows across 14 repos pin `tracebloc/.github/.github/workflows/*.yml@main`, and all of them pass `secrets: inherit`, so a single merge to this repo takes effect org-wide on the next workflow run and reaches every secret the calling repos hold. Adds @LukasWodka as owner of the workflows, the scripts they call, and the repository by default. This does not change the self-merge path: as sole owner he cannot approve his own PRs and still relies on `enforce_admins: false`. The control added here is over the other six members' changes to the enforcement layer.
…sting findings (#66) * fix(workflows): clear all 29 actionlint findings actionlint (with its native shellcheck pass) reported 29 findings across 8 of this repo's reusable workflows. None had ever been checked by CI. Fixed the real ones, suppressed the correct-code ones at the line. Real fixes: - advance-deploy-env.yml (SC2086): the git log range was an unquoted $RANGE. Rewritten as an array, NOT simply quoted -- on the first push to a branch the range is two argv entries (--max-count=50 and the SHA), so "$RANGE" would have handed git one bogus argument and broken PR discovery on that path entirely. Verified both push shapes produce identical PR lists before and after. - wip-limit-check.yml (SC2034): REPO_NAME was assigned and never used -- a copy-paste leftover from the workflows that do use it. Removed. - wip-limit-check.yml (SC2188): `> /tmp/items.txt` is a redirection with no command. Now `: > /tmp/items.txt`, matching the form already used in kanban-reconcile.yml. Byte-identical truncate. - kanban-reconcile.yml + fr-pass-comment.yml (SC2129): consecutive redirects to $GITHUB_OUTPUT grouped into a single `{ ... } >>` block. Verified byte-identical output. Suppressed as correct code (23x SC2016): single-quoted GraphQL documents where $org / $num / $p / $i / $f / $o are GraphQL variables interpolated server-side. Expansion is exactly what must NOT happen. Each gets a per-line `# shellcheck disable=SC2016` with the reason -- no file-wide directive and no actionlint config exclusion, so future code is not silently covered. kanban-reconcile.yml (SC2034, `label`): NOT dead code. It is the 4th `read` variable and exists to stop `read` folding field 4 of moves.tsv into $reason -- removing it would append "<TAB><category>" to every [OK]/[FAIL] log line. Kept, documented, suppressed. Behaviour is otherwise unchanged: the remaining diff is comments. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * ci(actionlint): lint this repo's own workflows on every PR This repo publishes 16 reusable workflows that every other repo consumes at `@main`, and it had no CI of its own -- nothing validated a workflow file before it merged and went org-wide. This closes that gap. actionlint parses each workflow, type-checks every ${{ }} expression, validates the runs-on / uses / needs wiring, and runs shellcheck over every `run:` block. A hard gate from day one (any finding fails the job), which is only affordable because the preceding commit cleared the backlog -- the tree is at zero findings, so the job is green and can be marked a required status check immediately. Notes: - actionlint is pinned by version AND verified against a pinned SHA-256 of the release tarball, rather than via a wrapper action that downloads it for us. actions/checkout is pinned to a full commit SHA. - A step asserts shellcheck is present: actionlint silently skips every shell check when the binary is missing, which would leave the gate green while checking far less. - Includes a concurrency group so a re-push supersedes the previous run. - Requests only `contents: read`. - The `paths:` filter means the job does not run on PRs touching no workflow file; the header documents what that implies before marking the check required. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * ci(actionlint): drop the paths filter so the check can be required A required status check with a `paths:` filter never runs on a PR that touches nothing matching it, and GitHub then waits forever for a status that cannot arrive -- so the PR is stuck. The job takes ~7s, which is far cheaper than that failure mode. Raised on the PR as the one thing to settle before marking it required. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
LukasWodka
commented
Jul 26, 2026
👋 Heads-up — Code review queue is at 41 / 30 Above the WIP limit. The team convention is to review existing PRs before opening new work. Open PRs currently in Code review (oldest first):
Pull from review before opening new work. (This is a nudge from the kanban WIP check, not a block.) |
LukasWodka
commented
Jul 26, 2026
The gate is right. The ladder is broken.
That is correct behaviour, and there is no legal way to satisfy it in this repo:
So the item is stuck at rank 7 against a required rank 9, permanently. This is
Why I am using the override rather than dragging cardsBoth get the same merge. The difference is that this one is on the PR, in the timeline, with a What this PR unblocksNothing here is live yet: all 96 caller workflows pin The real fixbackend#1274 (D8/D28) gives every repo a |
Uh oh!
There was an error while loading. Please reload this page.
Promotes the three fixes merged to
developtoday ontomain.This is the step that makes them real. All 96 caller workflows across 14 repos pin
tracebloc/.github/.github/workflows/*.yml@main, so until this lands the closure-router fix is notrunning, CODEOWNERS is not enforced, and actionlint is not linting anything.
Status=Prod. The*)default was inverted, so every hand-closed issue was being marked shipped — and becausefr-gatecompares by rank,Prod(10) satisfies every promotion gate.require_code_owner_reviews: truehas been set onmainwith no file behind it, so it resolved to nothing on the repo that gates every other repo.actionlintrequired onmain.developis 4 commits ahead ofmainand 0 behind — a clean fast-forward's worth of history, noconflicts.
After merging
actionlinta required check onmain. It is the first one this repo will ever have.set to
Prod.Part of RFC-BACKEND-0008 (tracebloc/backend#1262) D11/D18/D22, Phase 0.
Note
High Risk
Changes land on the org enforcement root at
@mainand alter kanban Status semantics that directly affect required FR promotion gates; misconfiguration could block merges or leave incorrect board state until reconcile runs.Overview
Promotes org-wide tracebloc/.github hardening to
main, where every other repo pins reusable workflows.Governance: New CODEOWNERS makes
require_code_owner_reviewsonmainreal for workflows and scripts. Newactionlintworkflow runs on every PR (nopathsfilter), installs a version- and digest-pinned binary, requires shellcheck, and fails on any workflow/shell finding.Security / process fix:
kanban-closure-routerno longer sets Status=Prod when an issue is closed as completed with no closing PR; it leaves kanban Status unchanged so manual closes cannot satisfyfr-gate. Routing logic is refactored to a singleSTATUSoutput; issue lookup usesISSUE_NUMBERin the step env.Correctness / lint cleanup:
advance-deploy-envpassesgit logranges as a bash array so the zero-beforefirst-push path gets separate--max-countand SHA arguments. Several workflows add targeted# shellcheck disable=SC2016on embedded GraphQL, plus small shell fixes (groupedGITHUB_OUTPUT,: >truncation inwip-limit-check, extrareadfield inkanban-reconcileapply step).Reviewed by Cursor Bugbot for commit 151cc3f. Bugbot is set up for automated code reviews on this repo. Configure here.