Uh oh!
There was an error while loading. Please reload this page.
feat(2036): add-to-kanban authenticates as the App — the last of 19 - #284
Conversation
The last of 19. Board writes stop running on one human's PAT. WHY THIS REPO WENT LAST. add-to-kanban.yml is a byte-compared COPY and .github's own copy is the source of truth caller-drift measures every other repo against. Migrating this one first would have made all 18 others read as drifted the moment the contract changed and before any of them could follow; migrating it last puts the drift window on develop, where it is a finding against work already in flight rather than against the fleet's main. The window is real and it is measured, not assumed. Read-only audit run 32227732563, dispatched just now: 17 repo-conformance drift finding(s). - averaging-service: add-to-kanban.yml has DRIFTED from the copy in tracebloc/.github (blob 07283e3 vs 603751a). ... 16 more, all the same finding This commit closes all 17 at once. release-train is correctly absent from that list -- its copy is `divergent` in the inventory with a written reason, so it was never compared. Byte-identical to the fleet by construction rather than by transcription: the file is the canonical blob copied verbatim, verified 07283e3 == 07283e3. Two known follow-ups, both already filed, so neither is discovered later: backend#2157 every mint added by this sweep requests the App's full permission set. It has to be one pass over all 19, since a byte-compared copy cannot be fixed piecemeal -- which is why this PR does NOT downscope. Raised by saadqbal. backend#2164 unrelated to this file; listed only because it is the other open item from the same review pass. make check: 41 selftests, green. Refs backend#2036. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
saadqbal
left a comment
There was a problem hiding this comment.
Byte-for-byte the fleet blob — same 603751a → 07283e3 pair as cli#525 and backend#2116, with the same secret names, the same owner: input and the same trigger. Also checked: the action SHA really is what v3.2.0 points at, the trigger is pull_request (not pull_request_target), there are no run: blocks at all so no github.event.* reaches a shell, the token only ever flows into add-to-project's input, and a failed mint fails the job rather than no-opping. And add-to-kanban.yml is a copies entry in repo-inventory.yml, not a reusable — no uses: tracebloc/.github/... callers anywhere, so nothing downstream sees this. Approving.
One thing worth sequencing before this lands: backend#2181 is open and adds three permission-* lines to backend's copy of this exact file. It's green now — it needed permission-issues: read and permission-pull-requests: read alongside the projects write, or add-to-project can't resolve the node it's adding. The moment it merges, backend drifts from the canonical this PR is establishing and one of the 17 findings you're closing re-opens. Either land those three lines across all 19 in one go, or mark backend divergent in repo-inventory.yml until the rest follow.
Nits, none blocking:
- caller-drift's
pushpaths don't includeadd-to-kanban.yml, so merging this won't re-run the audit. Worth a manual dispatch afterwards to show 17 → 0 rather than waiting for Monday's cron. - The added comment says this workflow fires on Dependabot PRs. True for the fleet, but this repo has no
dependabot.ymland has never had a Dependabot PR. Byte-identity wins so leave it as-is — just worth knowing a future reader shouldn't take it as evidence about this repo's secret scope. workflows/add-to-kanban.ymlat the repo root (not.github/workflows/, notworkflow-templates/) is a dead copy still onactions/add-to-project@v1.0.2andPROJECTS_KANBAN_TOKEN. GitHub never runs it and nothing references it, but after this it's the last PAT-to-the-board reference in the repo. Worth deleting separately.app-idcarriesdeprecationMessage: "Use 'client-id' instead."at the pinned SHA, so every run warns. Fleet-wide (8 other mints in this repo too), not this PR's job.
On #2157: you're right that downscoping doesn't close the head-rewrite vector. pull_request_target would — this job never checks out PR code, it only reads event metadata, so taking the workflow file from the base branch does the same work. Worth putting on that thread as the actual remediation.
Uh oh!
There was an error while loading. Please reload this page.
aptracebloc
left a comment
There was a problem hiding this comment.
Approving. The code is correct and I verified the central claims rather than reading them.
Byte-identity is empirically confirmed. PR head blob is 07283e30b80bddab05300d77b1f59cf719ac1b38; fetching add-to-kanban.yml from averaging-service, backend, frontend-app and tracebloc-engine on develop returns that exact sha. The "17" is right too: 18 required inventory entries minus .github itself, with release-train correctly excluded as divergent. Both action pins resolve to their claimed tags (bcd2ba49 = create-github-app-token v3.2.0, 5afcf98f = add-to-project v2.0.0).
The mint is proven working on this exact head. Because pull_request runs the workflow from the PR head, the add-to-project check is this new file executing — run 32228094633 shows the mint and the board write both green. No PAT fallback, no continue-on-error, so missing App secrets fail red. Trigger is pull_request (not pull_request_target) on a public repo with 0 forks, and GitHub withholds secrets from fork pull_request runs — so no fork exposure path. There is no run: block at all, so nothing can be spliced into a shell, and no permissions: block was widened.
Merge order is load-bearing
Blobs in play: A = 603751a0 (PAT, current .github/develop), B = 07283e30 (this PR's output, and all 17 copies today), C = 7830f7e0 (backend#2181's current output).
- This PR first: canonical
B, 17 copiesB→ 0 findings, fleet green. Then #2181 lands → 1 finding, which is its own deliberate validation window. - #2181 first: canonical still
A→ still 17 findings, one with a new reason.
So this merges before tracebloc/backend#2181. Worth flagging explicitly because #2181's body says "the .github canonical … merges last" — that refers to a laterB→C change which has no PR yet, not to this one. Reading it as "hold #284" inverts the correct order and keeps all 17 findings red for nothing.
One caution for the window after #2181 lands and before the fan-out completes: caller-drift's create-prs dispatch would propose rewriting backend's file back to canonical, reverting the least-privilege change. Worth not dispatching remediation until the fan-out is done.
Non-blocking
- "The last of 19" is stale on arrival — backend#2166 already plans a 20th change to this same file (
B→C), and its first step is open as #2181. Also worth knowing this PR does not retire the PAT:caller-drift.yml:166,178,231still usesPROJECTS_KANBAN_TOKENlive, for a documented reason. "Last of 19" ≠ "the PAT is gone." - The mint requests the App's full installation grant. Deferring is right — fixing it here re-opens 18-way drift, and
set-pr-status.ymlandkanban-reconcile.ymlcarry the identical unscoped mint, so it's pre-existing fleet posture rather than something introduced here. Being fixed in #2181 / #2166. GITHUB_TOKENis now unused by every step, sopermissions: {}would be ideal — byte-identity-blocked, belongs in the fan-out.- The comment says the workflow "also fires on DEPENDABOT PRs", but
.githubhas no dependabot config at any path. Inherent to the byte-identical copy model and accurate for the repos that do have one; noting it only so it isn't mistaken for a live case here.
One correction to something I wrote on #2181: I said a typo'd permission-* input is silently ignored, leaving full scope. @saadqbal established that's wrong — the action reads raw INPUT_PERMISSION-* env vars, so a misspelling 422s the mint. The fan-out does not need that spot-check.
— drafted with Claude Code
LukasWodka
commented
Aug 19, 2026
/fr-pass Best-effort FR passed (triage; behavioral evidence limited while e2e journey red — backend#2206). Advancing to Ready for prod. |
Refs backend#2036. The last of 19. Board writes stop running on one human's PAT.
Why this repo went last
add-to-kanban.ymlis a byte-compared copy, and.github's copy is the source of truthcaller-driftmeasures every other repo against. Migrating this one first would have made all 18 others read as drifted the moment the contract changed and before any of them could follow. Migrating it last puts the drift window ondevelop, where it is a finding against work already in flight rather than against the fleet'smain.The window was real, and it is measured
Dispatched the audit read-only just now — run 32227732563:
Its last scheduled run was 2026-08-18 07:18Z — before most of the sweep merged — so the fleet has been red for about a day with nothing reporting it. That is the cost of the ordering, and it was the cheaper of the two costs.
This commit closes all 17 at once.
release-trainis correctly absent from that list: its copy isdivergentin the inventory with a written reason (a repo-specific header, and it carries no Dependabot config), so it was never compared.Byte-identity by construction, not transcription
The file is the canonical fleet blob copied verbatim, then verified:
Worth doing that way round rather than hand-editing: the earlier sweep PRs each had their transform applied independently, and a copy guard that compares bytes will reject a single stray space. Checking the hash is one command; discovering the mismatch from an audit run a day later is not.
What this does NOT do
No
permission-*downscoping. saadqbal's finding is real and is filed as backend#2157 — every mint added by this sweep requests the App's full permission set. It is deliberately not fixed here, for two reasons:RELEASE_TRAIN_APP_IDand..._PRIVATE_KEYare org secrets atvisibility=all, and apull_requestrun uses the workflow file from the PR head — so branch-push access is enough to mint a token requesting whatever it likes. #2157 keeps least-privilege (real defence-in-depth: it bounds a compromised action or a bad pin) separate from the posture question, so closing the cheap half doesn't read as closing the expensive one.Verification
make check: 41 selftests, green · blob hash matches the fleet · the audit's 17 findings are all this one file, so this is the whole remaining set.Note
Low Risk
Single workflow auth swap for project board writes; behavior unchanged aside from credential source, with explicit failure if App secrets are missing.
Overview
Kanban automation no longer uses
PROJECTS_KANBAN_TOKENfor org project writes. It mints an org-scoped installation token for the tracebloc-release-train GitHub App (actions/create-github-app-tokenwithowner: ${{ github.repository_owner }}), then passes that token toactions/add-to-project.Inline comments document no PAT fallback (so a broken App path fails visibly) and that Dependabot-triggered runs need the same App secrets in Dependabot’s secret scope.
This is the canonical
add-to-kanban.ymlcopy the fleet audits for byte drift; aligning it closes the remaining drift findings from the backend#2036 sweep.Reviewed by Cursor Bugbot for commit 64b4ef5. Bugbot is set up for automated code reviews on this repo. Configure here.