Skip to content

feat(issue-triage-label-self): dogfood caller pinned at merged SHA - #195

Merged
kyle-sexton merged 1 commit into
mainfrom
feat/issue-triage-label-self
Jul 21, 2026
Merged

feat(issue-triage-label-self): dogfood caller pinned at merged SHA#195
kyle-sexton merged 1 commit into
mainfrom
feat/issue-triage-label-self

Conversation

@kyle-sexton

Copy link
Copy Markdown
Contributor

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

  • actionlint .github/workflows/issue-triage-label-self.yml
  • zizmor --persona=pedantic .github/workflows/issue-triage-label-self.yml (0 findings)
  • 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

… SHA

Closes the runtime-verification gap for issue-triage-label.yml: no
issues:opened/reopened trigger path existed in this repo before this
caller, so the reusable's label-set guard and fail-closed existence check
have only been exercised via local linting and an out-of-band API test, not
a real GitHub Actions event. This repo already carries a real `priority:
needs-triage` label, so the caller is a genuine end-to-end exercise, not a
stub.

Mirrors pr-issue-linkage-self.yml's shape: pinned to the reusable's merged
SHA (c5e729c, PR #191), not a PR head.

Closes #192

## Related
#191 (adds issue-triage-label.yml)
@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.
To continue using code reviews, you can upgrade your account or add credits to your account and enable them for code reviews in your settings.

@claude

claude Bot commented Jul 21, 2026

Copy link
Copy Markdown

Claude finished @kyle-sexton's task in 1m 55s —— View job


PR Review: feat(issue-triage-label-self): dogfood caller pinned at merged SHA

  • Gather context and understand request
  • Read changed file and reusable it calls
  • Review for correctness, security, and CLAUDE.md/REVIEW.md alignment
  • Post findings

Summary

This PR is clean. The new dogfood caller is correctly structured, the SHA pin is verified, permissions follow least-privilege, and the trigger contract matches what the reusable documents as required. No blocking or important findings.


SHA Pin — Verified ✅

The full SHA c5e729c0af0e55ffed4675ec85c1b57356fef79e resolves to the squash commit that merged #191 (c5e729c feat(issue-triage-label): reusable workflow…). Confirmed via git log. The trailing # c5e729c 2026-07-21 comment matches the CLAUDE.md pattern used across all self-callers.


Trigger Contract — Correct ✅

The reusable's header explicitly documents:

The caller owns the trigger and MUST use on: issues: types: [opened, reopened]

This caller does exactly that. The in-workflow guard (github.event.action == 'opened' || 'reopened') in the reusable provides a secondary safety net.


Permissions — Correct ✅

Level Grant Assessment
Workflow-level permissions: {} Zeros all defaults — correct
Job-level issues: write Minimum needed for getLabel + addLabels — correct

Per CLAUDE.md: "A called workflow can only downgrade the caller's GITHUB_TOKEN grant." The job grants issues: write; the reusable redeclares the same — no escalation possible.


No Concurrency Block — Appropriate ✅

Unlike pr-issue-linkage-self.yml (which needs it for synchronize/rapid-edit events), there's no meaningful race condition for issues: opened/reopened. An issue opens once; reopening is a discrete, infrequent event. Omitting concurrency: is the correct call here.


CLAUDE.md / REVIEW.md Alignment ✅

  • No pull_request_target or workflow_run with secrets — N/A (trigger is issues:). ✅
  • No untrusted checkout before any action step. ✅
  • No secrets forwarded; GITHUB_TOKEN flows automatically. ✅
  • No debug flags, no token echoing, no display_report/show_full_output concerns (not a review workflow). ✅
  • REVIEW.md blocking checks (authorization, injection, secrets in source, atomicity): none applicable to a single-job label-apply workflow with no user-controlled inputs. ✅

🟡 Nit — Implicit defaults leave caller intent undocumented

.github/workflows/issue-triage-label-self.yml L18–22

The caller omits the with: block entirely, silently inheriting:

label: 'priority: needs-triage'
label-prefix: 'priority:'

These defaults are correct for this repo today. However, if the reusable's defaults ever change (e.g., a label rename across adopters), this caller silently follows — there's nothing to grep for to find callers using the old value. Explicitly pinning with: inputs would make the intent self-documenting and make a future default-change audit trivial. Compare pr-issue-linkage-self.yml, which passes with: exempt-authors: even though a default exists.

Not a blocker — the PR description notes intentional use of defaults, and the defaults are empirically correct. Worth considering on the merge or as a follow-up. Fix this →


Verdict: Approve. The one nit above is genuinely optional given the PR's explicit rationale. The live verification step (open/reopen a real issue post-merge and confirm label application) documented in the test plan is the right remaining gate.

@claude

claude Bot commented Jul 21, 2026

Copy link
Copy Markdown

Claude finished @kyle-sexton's task in 1m 4s —— View job


Security Review

  • Read changed file
  • Analyze for security vulnerabilities
  • Post findings

No security issues found.

Reviewed .github/workflows/issue-triage-label-self.yml (22 lines, the only changed file) against the threat model in CLAUDE.md. Findings below.

Trigger — safe event

on: issues: types: [opened, reopened] runs in the base-repo context with a scoped GITHUB_TOKEN and no access to fork code. Neither pull_request_target nor workflow_run is used, eliminating the "pwn request" class of token-exfiltration vectors entirely.

Permissions — correctly minimal

permissions: {}                          # workflow-level: everything locked out
jobs:
  issue-triage-label:
    permissions:
      issues: write                      # job-level: only what addLabels needs

Top-level permissions: {} denies all grants by default. The single job re-opens only issues: write. Because a called workflow can only downgrade the caller's token, this satisfies the CLAUDE.md least-privilege rule. No contents, pull-requests, id-token, or other grants appear.

SHA pin — correct and complete

uses: …issue-triage-label.yml@c5e729c0af0e55ffed4675ec85c1b57356fef79e is the full 40-character SHA of the squash-merge commit for #191 (c5e729c in recent git log), not a mutable tag or branch ref. This blocks the mutable-tag supply-chain vector (cf. CVE-2025-30066).

No secrets, no shell execution, no untrusted input propagation

  • No secrets: inherit or explicit secrets: block — the reusable declares no secrets: interface, so none can be forwarded.
  • No with: inputs; caller relies on the reusable's safe defaults (priority: needs-triage / priority:).
  • No run: steps anywhere in the caller, eliminating script-injection surfaces.
  • Nothing from the event payload (github.event.issue.title, .body, etc.) is interpolated into any execution context.

Reusable verified in scope

I also read the referenced issue-triage-label.yml at the pinned SHA. Its github-script step passes caller inputs via env: (not via ${{ inputs.* }} directly inside the script: block), which is the correct injection-safe pattern. That file is outside this PR's diff and is not re-audited here beyond confirming the pin is valid.


Summary: The caller introduces no new attack surface. Trigger is safe, permissions are minimal, the pin is a full SHA at the merged commit, no secrets or untrusted input flows through. LGTM from a security standpoint.

@kyle-sexton

Copy link
Copy Markdown
Contributor Author

Tower merge (overnight delegation 2026-07-21, veto window open): dogfood self-caller mirroring sibling pattern, SHA-pin verified by review to resolve to #191's squash commit, least-privilege, all checks green. Live e2e trigger will be exercised immediately post-merge (reopen an unlabeled issue, verify the floor label lands).

This was generated by AI (control tower, session 6).

@kyle-sexton
kyle-sexton merged commit 0ce4a88 into main Jul 21, 2026
37 checks passed
@kyle-sexton
kyle-sexton deleted the feat/issue-triage-label-self branch July 21, 2026 10:12
kyle-sexton added a commit to melodic-software/standards that referenced this pull request Jul 21, 2026
…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)
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.

Add issue-triage-label-self.yml dogfood caller pinned at merged SHA

1 participant