Skip to content

org_reconcile_board_status: apply community and needs-attention labels - #12

Merged
silug merged 2 commits into
mainfrom
board-labels
Sep 3, 2026
Merged

org_reconcile_board_status: apply community and needs-attention labels#12
silug merged 2 commits into
mainfrom
board-labels

Conversation

@silug

@silug silug commented Sep 3, 2026

Copy link
Copy Markdown
Contributor

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 a User (not Bot) outside the org member list. Never removed. Applied here rather than per-repo because the Actions payload's author_association reports private org members as CONTRIBUTOR (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:

    Eligible Threshold
    community item 7d
    New, or no Status 7d
    In Review 14d
    In Progress 30d
    Todo / Ready / Parked / Blocked / Done never (label removed if present)

    "Human activity" = newest User-actored event among comments, reviews, commits, draft/ready flips, reopens, assignments, review requests, retitles — read from content.timelineItems. Bot actors are ignored (Renovate, CI, Copilot); LABELED/UNLABELED events are not fetched, so applying the label cannot bump the clock it measures and it cannot flap. The project item's updatedAt is 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 403 on 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.

⚠️ Before merging: App permissions

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_TOKEN PAT fallback would need repo scope). 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)

  • Status: 0 moves (board already reconciled)
  • 94 label changes: 24 community across 19 outside authors (jcpunk, greatflyingsteve, dforste, seriv, janbrono, nsballmann, marcogracklauer, jhoblitt, …); 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 (pupmod-simp-at#99).
  • Spot-checked three against live timelines: simplib#338 77d idle = last comment 2026-06-17 ✓; ssh#209 153d = last commit 2026-04-02 ✓; aide#172 unflagged = 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_dispatch with dry_run after 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-attention documented 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

  • Header documents the label rules, thresholds, activity definition and the new permissions; README gains the missing row for this workflow.
  • actionlint + shellcheck clean.

🤖 Generated with Claude Code

…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>
Comment thread .github/workflows/org_reconcile_board_status.yml Outdated
Comment thread .github/workflows/org_reconcile_board_status.yml Outdated
Comment thread .github/workflows/org_reconcile_board_status.yml
Comment thread .github/workflows/org_reconcile_board_status.yml Outdated
Comment thread .github/workflows/org_reconcile_board_status.yml
Comment thread .github/workflows/org_reconcile_board_status.yml Outdated
Comment thread .github/workflows/org_reconcile_board_status.yml Outdated
* 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>
@silug
silug merged commit 7d19a27 into main Sep 3, 2026
1 check passed
@silug
silug deleted the board-labels branch September 3, 2026 18:32
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.

2 participants