feat(issue-triage-label): reusable workflow to auto-apply needs-triage floor label - #191
Conversation
…e floor label
Applies the configured floor label (default `priority: needs-triage`) to an
issue opened or reopened with no label matching a configured prefix (default
`priority:`), so a forgotten priority label no longer silently drops an issue
out of the triage queue. Fail-closed on a missing label: the raw add-labels
endpoint auto-creates an unknown label instead of erroring (verified against
the live API), so this workflow calls GET .../labels/{name} first and fails
the job rather than let a bare, undefined label get created. Non-gating —
never blocks a merge.
Closes #155
## Related
Settled mechanism per melodic-software/claude-code-plugins#506's 2026-07-20
Class-B research-resolution comment.
|
You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard. |
|
Claude finished @kyle-sexton's task in 2m 46s —— View job PR Review
Overall: LGTM. The implementation is solid — all security ground rules from CLAUDE.md are followed, the logic is correct, and the non-obvious design decisions are well-documented. Two minor observations below; neither is a blocker. Security & CLAUDE.md compliance
Logic correctnessFail-closed label existence check — line 116–127 The Prefix guard reads from event payload — line 100–107
Step-level The
Observations (non-blocking)1. Caller example omits optional The README caller snippet (lines 183–193) shows only the minimal invocation. Callers who need non-default 2. No The PR body explains this clearly: pinning a newly-introduced workflow SHA at merge time requires a follow-up PR. The deferred READMEThe documentation entry is thorough, consistently styled with sibling entries, and covers all the non-obvious contract points (fail-closed behavior, loop safety, idempotency, triager responsibility for tier assignment). The internal reference link to |
|
Claude finished @kyle-sexton's task in 1m 28s —— View job Security Review
Scope: FindingsNo security vulnerabilities found. The implementation is clean across every attack surface I reviewed: Trigger safety — Script injection — The two Supply-chain / pin hygiene — Least-privilege — Top-level Fail-closed logic — The Loop safety — Documented and technically correct: label addition emits |
|
Tower merge (overnight delegation 2026-07-21, veto window open): merging under the operator's overnight decide-and-log delegation — non-gating reusable workflow, least-privilege ( This was generated by AI (control tower, session 6). |
) Closes #192 ## Summary Adds `.github/workflows/issue-triage-label-self.yml`, the dogfood caller for `issue-triage-label.yml` (#191), pinned to that reusable's merged squash SHA (`c5e729c`), not a PR head — mirroring `pr-issue-linkage-self.yml`'s pin discipline exactly. Why this exists: `issue-triage-label.yml` had no `issues: opened`/`reopened` trigger path anywhere in this repo before now, so its label-set guard and `getLabel` → 404 → `setFailed` fail-closed check were only ever exercised by local linting and an out-of-band API test in a disposable repo — not a real GitHub Actions event. This repo already carries a real `priority: needs-triage` label (confirmed via `gh label list`), so this caller is a genuine end-to-end exercise, not a stub. Locally verified clean: `actionlint`, `zizmor --persona=pedantic` (0 findings), `editorconfig-checker`, `typos`. ## Related #191 (adds `issue-triage-label.yml`, merged at `c5e729c0af0e55ffed4675ec85c1b57356fef79e`). ## Test plan - [x] `actionlint .github/workflows/issue-triage-label-self.yml` - [x] `zizmor --persona=pedantic .github/workflows/issue-triage-label-self.yml` (0 findings) - [x] `editorconfig-checker` / `typos` over the changed file - [ ] Live verification: open or reopen a real issue in this repo post-merge with no `priority:*` label and confirm `priority: needs-triage` is applied by this caller 🤖 Generated with [Claude Code](https://claude.com/claude-code)
…ontract (#236) Closes #235 ## Summary Registers ci-workflows' `issue-triage-label` reusable workflow as a reviewed `runner-input` contract in `components/runner-policy/policy.json`, pinned to its merged SHA `c5e729c0af0e55ffed4675ec85c1b57356fef79e`: ```json "melodic-software/ci-workflows/.github/workflows/issue-triage-label.yml@c5e729c0af0e55ffed4675ec85c1b57356fef79e": { "routing": "runner-input", "runnerInput": "runner", "allowedInputs": ["runner", "label", "label-prefix"], "allowedSecrets": {}, "allowedCallerPermissions": { "issues": "write" } } ``` - `allowedInputs` covers the reusable's full `workflow_call.inputs` surface (`runner`, `label`, `label-prefix`) — a caller may pass any subset. - `allowedSecrets: {}` — the reusable takes no secrets. - `allowedCallerPermissions: { issues: write }` — the reusable's job needs exactly `issues: write` to read and add labels; no `contents` (no checkout). - No `selectorResultInput` — the reusable has no chained-selector (`prerequisite-result`-style) input. Locally verified: `npm run test:runner-policy` (227/227 passing), `npm run lint:runner-policy` ("Runner policy passed."), `npm run lint:md`, `npm run lint:hooks`, plus `lefthook`'s pre-commit gate (typos, gitleaks, editorconfig, biome) on commit. ## Related - melodic-software/ci-workflows#191 — the reusable workflow, merged. - melodic-software/ci-workflows#195 — its dogfood self-caller, merged, pinned at this same merged SHA. - melodic-software/ci-workflows#192 — end-to-end proof the self-caller actually applies the floor label live. - melodic-software/claude-code-plugins#850 — the consumer PR blocked on this contract (its `Runner policy` check fails without it). - melodic-software/claude-code-plugins#506 — the issue driving that caller. ## Test plan - [x] `npm run test:runner-policy` (227 passing) - [x] `npm run lint:runner-policy` ("Runner policy passed.") - [x] `npm run lint:md` - [x] `npm run lint:hooks` - [x] `lefthook` pre-commit gate (typos, gitleaks, editorconfig, biome)
…850) Closes #506 ## Summary Adds `.github/workflows/issue-triage-label.yml`, a thin caller invoking ci-workflows' `issue-triage-label` reusable workflow (`workflow_call`) on `issues: opened`/`reopened`. Applies the existing `priority: needs-triage` label when an issue carries no `priority:*` label; leaves issues filed with an explicit tier untouched. Non-gating. - Pinned by merged SHA `c5e729c0af0e55ffed4675ec85c1b57356fef79e` (ci-workflows PR #191), matching this repo's existing SHA-pin convention (`pr-issue-linkage.yml`, `do-not-merge.yml`, `link-check.yml`). - Uses the reusable's defaults for `label` (`priority: needs-triage`) and `label-prefix` (`priority:`) — both already match this repo's taxonomy, so no overrides needed. `runner: ubuntu-24.04` passed explicitly per sibling caller convention. - Verified live: `priority: needs-triage` exists in this repo's label set (`gh label list`), so the reusable's fail-closed missing-label guard will not trip. - Locally verified clean: `actionlint`, `zizmor --persona=pedantic` (0 findings after adding an explanatory comment on the `issues: write` grant), `typos`. ## Related melodic-software/ci-workflows#191 (merged 2026-07-21T10:06:31Z) implements the reusable workflow this caller invokes, carrying the settled mechanism from #506's 2026-07-20 Class-B research-resolution comment and the tower decision comment confirming this thin-caller shape. ## Test plan - [x] `actionlint .github/workflows/issue-triage-label.yml` - [x] `zizmor --persona=pedantic .github/workflows/issue-triage-label.yml` (0 findings) - [x] `typos .github/workflows/issue-triage-label.yml` - [x] `gh label list` confirms `priority: needs-triage` exists live in this repo
Closes #155
Summary
Adds
.github/workflows/issue-triage-label.yml, aworkflow_callreusableworkflow that applies a configured floor label (default
priority: needs-triage) to an issue opened or reopened with no label matching aconfigured prefix (default
priority:). Non-gating — it never fails a PR orblocks a merge; it only guarantees new issues don't silently drop out of the
triage queue for lack of a label.
the settled mechanism.
and verified empirically against the live API in a disposable throwaway
repo before writing this): the raw
POST .../issues/{n}/labelsendpointdoes not 404 on an unknown label name — it silently auto-creates a
bare label (default gray color, no description). Relying on that call to
fail naturally would violate the "never define a label" contract, so this
workflow calls
GET /repos/{owner}/{repo}/labels/{name}first and hard-failsthe job if that 404s, before ever calling
addLabels.permissions: {}, job grants onlyissues: write. Usesactions/github-script(matching this repo's existingpattern in
pr-issue-linkage.yml/do-not-merge-gate.yml), SHA-pinned.same caller-example + rationale shape as
pr-issue-linkage.ymlanddo-not-merge-gate.yml.Locally verified clean:
actionlint,zizmor --persona=pedantic(0findings),
markdownlint-cli2(README),typos,editorconfig-checker,and a
node --checksyntax pass on the embedded script.Conventions followed
pr-issue-linkage.yml/pr-issue-linkage-self.yml(PR feat(pr-issue-linkage): add exempt-authors opt-in for bot PRs #171'snewest precedent),
do-not-merge-gate.yml,pulumi-version-drift-check.yml,and
link-check.ymlfor the reusable-workflow shape, inputs contract,pinned-SHA
actions/github-scriptusage, and README documentation style.runnerinput included per convention; noprerequisite-resultinput —that pattern is specific to gating required-checks chained after a
prerequisite job, and this workflow is non-gating.
Ambiguities / decisions worth a second look
issues.yml; every existingreusable workflow here is named after its concern (
do-not-merge-gate,pr-issue-linkage,link-check), not its triggering event, so I namedthis
issue-triage-label.ymlinstead. Flagging as veto-able rather thanassuming.
-self.ymldogfood caller in this PR. Every existing reusable herehas a same-repo dogfood caller, and this repo already carries a real
priority: needs-triagelabel — so a self-caller would be a genuineend-to-end test. But every existing
-self.ymlpins its reusable at analready-merged SHA (repo-wide pin-by-SHA policy), which is impossible
for a workflow introduced in this same PR without either an unpinned
reference (policy violation) or a relative-path call (inconsistent with
every other caller here). Deferring
issue-triage-label-self.ymlto afollow-up PR once this one merges and a SHA exists to pin.
claude-code-pluginsis notincluded here — tracked separately, gated on this landing.
Related
Settled mechanism per melodic-software/claude-code-plugins#506's 2026-07-20
Class-B research-resolution comment. Mirrors the #510 → #154 cross-repo
filing precedent.
Test plan
actionlint .github/workflows/issue-triage-label.ymlzizmor --persona=pedantic .github/workflows/issue-triage-label.yml(0 findings)markdownlint-cli2 README.mdtypos/editorconfig-checkerover changed filesnode --checkon the extracted embedded scriptPOST .../issues/{n}/labelsauto-createbehavior and
GET .../labels/{name}404 behavior in a disposableprivate repo (not this workflow itself — no live trigger path exists
pre-merge; tracked as the residual gap the follow-up
-self.ymlcloses)🤖 Generated with Claude Code