org_reconcile_board_status: apply community and needs-attention labels - #12
Merged
Conversation
…abels Adds a second pass, after Status, over the same board snapshot (simp/puppetsync#98 and the staleness extension of #99). `community` (sticky) goes on every open issue or PR whose author is a human outside the org member list. It is applied here rather than by the per-repo intake workflow because the event payload's author_association reports private org members as CONTRIBUTOR -- 20 of simp's 32 members -- so a per-repo step would have labelled most of the team's own work as outside contributions. The job already resolves membership authoritatively for the Status mapping. `needs-attention` (transient) is applied when an item has had no human activity past its column's threshold (community 7d, New / no Status 7d, In Review 14d, In Progress 30d; never for Todo/Ready/Parked/Blocked/Done) and removed as soon as it has. "Human activity" is the newest User-actored event among comments, reviews, commits, draft/ready flips, reopens, assignments, review requests and retitles, read from the content's timeline; Bot actors are ignored and LABELED events are not fetched at all, so applying the label cannot reset the clock it measures. The project item's own updatedAt is never consulted (it is floored at 2024-01-25 by past bulk operations). Nothing is ever closed. Writes use the REST labels endpoints, which serve issues and PRs alike. A permission failure aborts the labels pass with grant guidance after the Status pass has completed, so the newer pass cannot hold the older one hostage. The App therefore now needs Issues and Pull requests at Read and write. Dry-run against the live board (171 items): 0 Status moves, 94 label changes -- 24 `community` across 19 outside authors, 69 `needs-attention` (20 In Progress, 13 In Review, 20 New, 16 no Status), 1 removal of a hand-applied `needs-attention` from a Todo item. Spot-checked three against live timelines; all idle counts matched. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* Membership is tested by equality (`any($members[]; . == $login)`), not jq's substring-matching `inside`, in both passes and the log line. An outside `trevor` was a member if `trevor-vaughan` was. * The activity window is 100 events deep (the API maximum). If it is full and holds no human event, idleness is known only as a lower bound: the label is added when even that exceeds the threshold, and otherwise the item is left exactly as it is. Previously a bot-busy PR fell back to its creation date and was flagged as stale while being actively discussed. * Thresholds are keyed off the Status the first pass just set (moves.tsv is folded into the snapshot), so a New -> In Review promotion no longer adds the label against 7d and removes it against 14d an hour later. * `needs-attention` is documented as job-owned: it is removed from any column without a threshold, hand-applied or not, and the header says so. Removal now requires a positive signal (closed, no threshold, or human activity KNOWN to be inside the threshold); a lower bound below the threshold proves nothing and leaves the label alone. * Writes are paced at one per second and retried on GitHub's secondary rate limit, which arrives as HTTP 403 and was being read as a permission failure that aborted the pass. * Only a 404 whose body says "Label does not exist" counts as a completed removal; a bare "Not Found" is how GitHub reports an unreachable issue or repo and is a failure. * POST .../labels silently creates a missing label, so each (repo, label) pair is confirmed to exist once per run before any add; a repo without it is a per-item failure. The header no longer claims the write fails. Dry run against the live board: 93 label changes (24 community, 68 needs-attention, 1 removal); the only difference from the pre-review run is one item a human moved to Parked in between. No item hit the saturated-window case today. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
michael-riddle
approved these changes
Sep 3, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Implements the labelling half of simp/puppetsync#98 and the staleness extension of simp/puppetsync#99 as a second pass in the existing reconciler. Companion to simp/puppetsync#104, which carries the intake filter (#97) and explains why #98 was moved here.
What it does
Every hour, after the Status pass and from the same board snapshot:
community(sticky) — added to every open issue/PR whose author is aUser(notBot) outside the org member list. Never removed. Applied here rather than per-repo because the Actions payload'sauthor_associationreports private org members asCONTRIBUTOR(20 of simp's 32 members are private), which would have permanently labelled most of the team's work as outside contributions. This job already resolves membership authoritatively.needs-attention(transient) — added when an item has had no human activity past its column's threshold, removed as soon as it has:communityitem"Human activity" = newest
User-actored event among comments, reviews, commits, draft/ready flips, reopens, assignments, review requests, retitles — read fromcontent.timelineItems.Botactors are ignored (Renovate, CI, Copilot);LABELED/UNLABELEDevents are not fetched, so applying the label cannot bump the clock it measures and it cannot flap. The project item'supdatedAtis never consulted (floored at 2024-01-25 by bulk operations). Nothing is ever closed.Writes go through the REST labels endpoints (issues and PRs alike). A
403on the first label write aborts the labels pass with grant guidance after the Status pass has completed, so a permission gap in the new pass can't hold the existing one hostage. Per-item failures (e.g. a repo missing the label) are reported and the run continues.The App currently has Issues/Pull requests at Read. The labels pass needs Repository permissions → Issues: Read and write and Pull requests: Read and write (the
SIMP_PROJECT_TOKENPAT fallback would needreposcope). Without it, every hourly run will complete Status and then fail on the first label write.Dry run against the live board (171 items, via a member PAT)
communityacross 19 outside authors (jcpunk, greatflyingsteve, dforste, seriv, janbrono, nsballmann, marcogracklauer, jhoblitt, …); 69needs-attention— 20 In Progress, 13 In Review, 20 New, 16 no Status; 1 removal of a hand-appliedneeds-attentionfrom a Todo item (pupmod-simp-at#99).simplib#33877d idle = last comment 2026-06-17 ✓;ssh#209153d = last commit 2026-04-02 ✓;aide#172unflagged = review 8d ago ✓.That first-run volume is the honest picture the board plan predicted (a flat 14d rule would have flagged 83%). Recommend a
workflow_dispatchwithdry_runafter granting permissions to see the table in the step summary before the first live tick.Review follow-ups (0cbc96e)
Equality-based membership test; 100-deep activity window with saturation detection (lower-bound idleness never adds or removes on a guess); thresholds keyed off the post-move Status;
needs-attentiondocumented as job-owned; paced writes with secondary-rate-limit retry; strict "Label does not exist" 404 on removal; per-repo label existence check so the job never creates a label. Re-run dry run: 93 changes, differing from the first only by an item a human parked in between.Also
🤖 Generated with Claude Code