Skip to content

chore(kanban): reconcile board membership, not just status - #93

Merged
LukasWodka merged 4 commits into
developfrom
chore/reconcile-membership-sweep
Jul 28, 2026
Merged

chore(kanban): reconcile board membership, not just status#93
LukasWodka merged 4 commits into
developfrom
chore/reconcile-membership-sweep

Conversation

@LukasWodka

@LukasWodkaLukasWodka commented Jul 28, 2026

Copy link
Copy Markdown
Contributor

What

Extends the weekly kanban reconciler (kanban-reconcile.yml) from status reconciliation to also do membership reconciliation.

Why

add-to-kanban.yml is event-driven and fire-and-forget: one shot on a PR/issue opened event, no retry, no reconciliation. A single dropped webhook leaves a permanent silent gap — the item is never on the board and nothing ever catches it.

Found concretely this week: tracebloc-engine#511 (a real merged PR) was never added, which blocked engine's dev→staging promotion via the fr-gate. An audit of the 5 release-train repos found 3 more of the same class (backend#1191, engine#473, client-runtime#165) — ~1.5% of recent PRs. The existing reconciler couldn't help: it only reconciles the Status of items already on the board.

How

A new step sweeps every board-tracked repo (those with the add-to-kanban.yml caller — self-maintaining, so it never pulls in repos that deliberately don't track, e.g. release-train's own PRs) for:

  • open PRs/issues, and
  • PRs merged in the last 21 days

with no project #2 entry, and adds each in a sensible starting column:

ItemColumn
open PR (non-draft)Code review
open PR (draft)In progress
open issueBacklog
merged PROn dev / FR on staging / Prod (by branch containment)

Archived items count as present (includeArchived: true), so the daily archiver is never fought. Honors the existing dry-run input. Safety cap: aborts if > 60 items look missing (that's a token/scope bug, not real drift).

Test plan

  • actionlint clean locally.
  • After merge + promotion to main, dispatch with dry-run = true and confirm the planned adds look right before the first live weekly run.

Note

Scheduled workflows run from the default branch (main), so this only takes effect on the weekly cron once promoted develop→main (same as the earlier reconciler/gate changes). Happy to promote on your go.

🤖 Generated with Claude Code


Note

Medium Risk
Automated GraphQL adds/archives on the production kanban can affect release gates if mis-scoped; runaway guards and repo scoping limit blast radius but wrong column assignment could still confuse reviewers briefly.

Overview
Extends the weekly kanban reconcile workflow so it heals gaps on project #2, not only wrong Status values on cards already on the board.

A new membership step runs after status reconcile (even if classify/apply aborts). It discovers repos that ship add-to-kanban.yml, searches open items plus PRs merged in the last 21 days with no project #2 row (archived cards count as present), skips release-train/* heads, and adds missing issues/PRs with an initial column (Backlog / In progress / Code review / On dev / FR on staging / Prod from branch containment). It honors dry-run and aborts if more than 60 adds are planned.

Status reconcile changes: resolves extra Status options; pulls Backlog, In progress, and No status so closed release-train promotion PR cards can be archived while other pre-merge items are left alone; apply handles ARCHIVE via archiveProjectV2Item. Safety docs now cap both moves (>100) and adds (>60).

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

Update: promotion-PR hygiene

Release-train promotion PRs (head: release-train/*) are plumbing: the membership sweep now never adds them, and the weekly classify pass archives their cards once the PR is closed/merged (open promotion PRs are left alone — the train manages them). Pairs with the fr-gate transparency fix (#94/#95) so plumbing neither blocks the prod hop nor clutters the board.

add-to-kanban is fire-and-forget (one shot on 'opened', no retry), so a
single dropped webhook leaves a permanent silent gap - e.g. tracebloc-engine#511
was never on the board, which blocked its dev->staging promotion. The weekly
reconciler previously only fixed the Status of items already on the board; it
now also sweeps every board-tracked repo for open + recently-merged PRs/issues
with no project #2 entry and adds them in the right starting column (open PR ->
Code review, draft -> In progress, issue -> Backlog, merged -> On dev/FR on
staging/Prod by containment). Archived items count as present, so the daily
archiver is never fought. Cap: aborts if > 60 look missing (scope bug guard).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@LukasWodka

Copy link
Copy Markdown
ContributorAuthor

bugbot run

Comment thread.github/workflows/kanban-reconcile.yml Outdated
The arg is valid and defaults to true (verified via schema introspection),
so Bugbot's 'unknown argument fails the query' finding was a false positive -
the search query runs fine. Dropping it anyway: it is redundant and matches
the sibling workflows' plain projectItems(first: N). Behaviour is unchanged
(archived cards still count as present, so the daily archiver isn't fought).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@LukasWodka

Copy link
Copy Markdown
ContributorAuthor

bugbot run

Comment thread.github/workflows/kanban-reconcile.yml
…add them
Release-train promotion PRs (head release-train/*) are plumbing, not work
items. The membership sweep now skips them, and the classify step archives
their board cards once the PR is closed/merged (open ones are left to the
train). Keeps every promotion cycle from stranding 3-5 cards in FR on
staging that nobody functional-reviews.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

@cursorcursorBot 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.

There are 2 total unresolved issues (including 1 from previous review).

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 851937b. Configure here.

Comment thread.github/workflows/kanban-reconcile.yml
…romotion cards in any column
Bugbot round 2: (1) the membership sweep now runs under if: always() gated
only on the ids step, so a MAX_MOVES abort in status-classify no longer
skips the healing pass; (2) the item pull now includes Backlog / In
progress / No status solely so closed promotion-PR cards hiding there get
archived -- classify explicitly skips everything else in those columns, so
status-reconcile semantics are unchanged.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@LukasWodka
LukasWodka merged commit 8f9fbaf into developJul 28, 2026
2 checks passed
LukasWodka added a commit that referenced this pull request Jul 28, 2026
chore(promote): develop → main — kanban membership reconcile (#93)
@LukasWodka
LukasWodka deleted the chore/reconcile-membership-sweep branch August 1, 2026 21:45
LukasWodka added a commit that referenced this pull request Aug 20, 2026
…source of truth)
Byte-identical to the 16 copies already merged, which is the point: this file is
what caller-drift measures every other repo against.
ORDERING INVERTED ON PURPOSE, and the reason is worth stating because it
contradicts the heuristic this sweep started with. "Source of truth last"
minimises the PEAK number of drift findings while most repos are unswept. Once the
majority has flipped it does the opposite -- holding this file back is now the only
thing keeping the audit red:
.github unscoped (today) 16 findings the 16 merged copies all differ
.github scoped (this PR) 1 finding only cli remains, its PR still open
So this lands BEFORE the last straggler rather than after it. The heuristic was
right for the first sixteen PRs and wrong for the seventeenth.
WHAT IS NOT IN THIS COMMIT. repo-inventory.yml's `divergent` reason for
release-train says its header "is now the ONLY difference". That is still false --
release-train's copy also lacks the scoping until release-train#93 merges, and it
carries a second comment difference (the Dependabot note) the reason does not
mention. Editing it here would make the inventory claim something untrue about a
file that has not changed yet. It follows #93.
Refs backend#2157.
LukasWodka added a commit that referenced this pull request Aug 20, 2026
…source of truth) (#286)
Byte-identical to the 16 copies already merged, which is the point: this file is
what caller-drift measures every other repo against.
ORDERING INVERTED ON PURPOSE, and the reason is worth stating because it
contradicts the heuristic this sweep started with. "Source of truth last"
minimises the PEAK number of drift findings while most repos are unswept. Once the
majority has flipped it does the opposite -- holding this file back is now the only
thing keeping the audit red:
.github unscoped (today) 16 findings the 16 merged copies all differ
.github scoped (this PR) 1 finding only cli remains, its PR still open
So this lands BEFORE the last straggler rather than after it. The heuristic was
right for the first sixteen PRs and wrong for the seventeenth.
WHAT IS NOT IN THIS COMMIT. repo-inventory.yml's `divergent` reason for
release-train says its header "is now the ONLY difference". That is still false --
release-train's copy also lacks the scoping until release-train#93 merges, and it
carries a second comment difference (the Dependabot note) the reason does not
mention. Editing it here would make the inventory claim something untrue about a
file that has not changed yet. It follows #93.
Refs backend#2157.
LukasWodka added a commit that referenced this pull request Aug 20, 2026
saadqbal approved with one fix, and it is the file's own rule turned on itself.
THE PROSE SAID 13 IN TWO PLACES while `EXEMPT` holds 12. His diagnosis is right:
13 is the mint STEP count the audit reports -- 12 unscoped plus one already scoped
-- so the two are different populations, and the smaller one drifts the moment a row
is burnt down. Which is the point of the guard.
And his recommended fix is the right one: not "write 12", but let the number come
from `len(_exempt())`, which the run already prints. A hardcoded tally sitting
directly above the list it counts is the exact pattern backend#1729 is cited for --
in the file that cites it. Both prose sites now carry no number, and the docstring
says why, so the next person does not helpfully add one back.
MERGE CONFLICT resolved against develop, which moved three times underneath this
branch (#288, #289, #291). All three hunks wanted BOTH sides, not one:
Makefile `lint` mint-scope AND mutation-house-rules-dry
SELFTEST_TARGETS selftest-mint-scope AND selftest-house-rules
selftests.yml the mint-scope audit step AND the house-rules mutation step
Makefile CI map one line naming all three, since the required `selftests`
context now runs all of them
repo-inventory.yml kept OURS deliberately: develop still carries the
pre-release-train#93 wording ("the header alone"), which is now false -- #93 added
the scoping, so the divergence really is two comment blocks and nothing else, and
ours is the version with the checkable claim in it.
Two comments in selftests.yml also lost their case counts on the way through, for
the same reason as the docstring: `mint-scope` prints its own totals.
make check exit 0. mint-scope: 13 steps, 12 exempted, 0 findings. 41 house-rules
cases. mint-scope selftest 13/13.
Refs backend#2157.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Sign up for freeto 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.

1 participant

@LukasWodka