diff --git a/README.md b/README.md index 20ed963..f5dca11 100644 --- a/README.md +++ b/README.md @@ -129,7 +129,7 @@ flowchart LR | [Dependency Sweeper](patterns/dependency-sweeper.md) | 6h–1d | [dependency-sweeper](starters/dependency-sweeper/) | L2 patch-only | Medium | | [Changelog Drafter](patterns/changelog-drafter.md) | 1d or tag | [changelog-drafter](starters/changelog-drafter/) | **L1** draft | Low | | [Post-Merge Cleanup](patterns/post-merge-cleanup.md) | 1d–6h | [post-merge-cleanup](starters/post-merge-cleanup/) | **L1** off-peak | Low | -| [Issue Triage](patterns/issue-triage.md) | 2h–1d | [minimal-loop](starters/minimal-loop/) | **L1** propose-only | Low | +| [Issue Triage](patterns/issue-triage.md) | 2h–1d | [issue-triage](starters/issue-triage/) | **L1** propose-only | Low | Not sure which to pick? Try the [interactive picker](https://cobusgreyling.github.io/loop-engineering/#interactive) or [pattern-picker](docs/pattern-picker.md). diff --git a/STATE.md b/STATE.md index bc6f961..b2e5b83 100644 --- a/STATE.md +++ b/STATE.md @@ -11,8 +11,8 @@ Last run: 2026-06-17T12:08:32Z (automated daily-triage workflow) ## Watch List - Expand contributor failure stories (dependency sweeper, multi-loop). -- Collect a production story for Post-Merge Cleanup. - Validate `loop-init` scaffolds on fresh projects across all patterns. +- Dogfood Issue Triage starter on this repo (L1 propose-only). ## Recent Noise (ignored this run) diff --git a/examples/README.md b/examples/README.md index 3e75fcf..6b2414d 100644 --- a/examples/README.md +++ b/examples/README.md @@ -22,7 +22,7 @@ Start with [primitives-matrix.md](../docs/primitives-matrix.md) to map capabilit | Post-Merge Cleanup | [grok/post-merge-cleanup.md](./grok/post-merge-cleanup.md) | [claude-code/post-merge-cleanup.md](./claude-code/post-merge-cleanup.md) | [codex/post-merge-cleanup.md](./codex/post-merge-cleanup.md) | [github-actions/post-merge-cleanup.yml](./github-actions/post-merge-cleanup.yml) | | Dependency Sweeper | [grok/dependency-sweeper.md](./grok/dependency-sweeper.md) | [claude-code/dependency-sweeper.md](./claude-code/dependency-sweeper.md) | [codex/dependency-sweeper.md](./codex/dependency-sweeper.md) | [github-actions/dependency-sweeper.yml](./github-actions/dependency-sweeper.yml) | | Changelog Drafter | [grok/changelog-drafter.md](./grok/changelog-drafter.md) | [claude-code/changelog-drafter.md](./claude-code/changelog-drafter.md) | [codex/changelog-drafter.md](./codex/changelog-drafter.md) | [github-actions/changelog-drafter.yml](./github-actions/changelog-drafter.yml) | -| Issue Triage | [grok/issue-triage.md](./grok/issue-triage.md) | [claude-code/issue-triage.md](./claude-code/issue-triage.md) | [codex/issue-triage.md](./codex/issue-triage.md) | — (see daily-triage.yml as template) | +| Issue Triage | [grok/issue-triage.md](./grok/issue-triage.md) | [claude-code/issue-triage.md](./claude-code/issue-triage.md) | [codex/issue-triage.md](./codex/issue-triage.md) | [github-actions/issue-triage.yml](./github-actions/issue-triage.yml) | L2 patterns ship multi-tool skills inside one starter folder — see `starters//`. diff --git a/examples/claude-code/issue-triage.md b/examples/claude-code/issue-triage.md index e6bae8b..53e79bf 100644 --- a/examples/claude-code/issue-triage.md +++ b/examples/claude-code/issue-triage.md @@ -10,12 +10,13 @@ Same pattern as Grok; uses `$issue-triage` skill invocation and Claude Code sche ## Skills Setup -Copy and adapt triage instructions to `.claude/skills/issue-triage/SKILL.md`: +Scaffold or copy the skill: ```bash +npx @cobusgreyling/loop-init . --pattern issue-triage --tool claude +# Or manually: mkdir -p .claude/skills/issue-triage -# Start from templates/SKILL.md.loop-triage — retarget for GitHub issues + discussions -cp templates/SKILL.md.loop-triage .claude/skills/issue-triage/SKILL.md +cp templates/SKILL.md.issue-triage .claude/skills/issue-triage/SKILL.md ``` Add verifier for L2 graduation: @@ -52,7 +53,7 @@ Human gate remains on: `P0`, `P1`, `security`, `breaking-change`. ## GitHub Action Fallback -For event-driven triage on new issues, see [examples/github-actions/daily-triage.yml](../github-actions/daily-triage.yml) as a template — add `issues: opened` trigger and issue-triage prompt. +For event-driven triage on new issues, see [examples/github-actions/issue-triage.yml](../github-actions/issue-triage.yml). ## References diff --git a/examples/codex/issue-triage.md b/examples/codex/issue-triage.md index 6527fac..11f1341 100644 --- a/examples/codex/issue-triage.md +++ b/examples/codex/issue-triage.md @@ -33,7 +33,7 @@ Flag anything touching auth, payments, or public API for human review. ## Skills -Install `issue-triage` per [Codex Agent Skills](https://developers.openai.com/codex/skills) — same `SKILL.md` format. Start from `templates/SKILL.md.loop-triage` and adapt scanning rules for your issue tracker. +Install `issue-triage` per [Codex Agent Skills](https://developers.openai.com/codex/skills) — scaffold with `loop-init` or copy `templates/SKILL.md.issue-triage` to `.codex/skills/issue-triage/SKILL.md`. Define light verifier in `.codex/agents/verifier.toml` before enabling L2. diff --git a/examples/github-actions/README.md b/examples/github-actions/README.md index e00232d..2d7ea09 100644 --- a/examples/github-actions/README.md +++ b/examples/github-actions/README.md @@ -27,6 +27,7 @@ The state file schema and skills are tool-agnostic — only the invocation step | [daily-triage.yml](./daily-triage.yml) | Cron weekdays | Daily Triage | | [ci-sweeper.yml](./ci-sweeper.yml) | `workflow_run` failure | CI Sweeper | | [post-merge-cleanup.yml](./post-merge-cleanup.yml) | Push to main + nightly | Post-Merge Cleanup | +| [issue-triage.yml](./issue-triage.yml) | Cron 2h weekdays + `issues` events | Issue Triage | ## Security diff --git a/examples/github-actions/issue-triage.yml b/examples/github-actions/issue-triage.yml new file mode 100644 index 0000000..613f8e7 --- /dev/null +++ b/examples/github-actions/issue-triage.yml @@ -0,0 +1,53 @@ +# Issue Triage — GitHub Actions (tool-agnostic trigger) +# Scans open issues on a cadence and on new issues. L1 propose-only — updates +# issue-triage-state.md. Wire the "Run triage agent" step to your harness. + +name: Issue Triage Loop + +on: + schedule: + - cron: '0 */2 * * 1-5' # Every 2h weekdays (UTC) + issues: + types: [opened, reopened, labeled] + workflow_dispatch: + +permissions: + contents: read + issues: read + pull-requests: read + +jobs: + triage: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v6 + + - name: Gather issue context + id: issues + run: | + echo "Open issues (last 20):" + gh issue list --state open --limit 20 --json number,title,labels,createdAt,updatedAt 2>/dev/null || true + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + - name: Ensure issue-triage-state.md exists + run: | + if [ ! -f issue-triage-state.md ]; then + cp starters/issue-triage/issue-triage-state.md.example issue-triage-state.md 2>/dev/null || \ + echo "# Issue Triage State\n\nLast run: never\nOpen actionable: 0\n\n## Top 5\n\n## Proposed Labels (L1 — not applied)\n" > issue-triage-state.md + fi + + # Replace with your agent invocation (Codex CLI, repository_dispatch, Grok/Claude runner) + - name: Run issue-triage agent + run: | + echo "::notice::Wire this step to your agent harness." + echo "Prompt: Run issue-triage skill. Update issue-triage-state.md." + echo "Week 1: propose labels and priority only — no auto-apply or close." + echo "See examples/github-actions/README.md and examples/grok/issue-triage.md" + + - name: Upload state artifact + uses: actions/upload-artifact@v4 + with: + name: issue-triage-state + path: issue-triage-state.md + if-no-files-found: ignore \ No newline at end of file diff --git a/examples/grok/issue-triage.md b/examples/grok/issue-triage.md index 4f5e631..4dc473e 100644 --- a/examples/grok/issue-triage.md +++ b/examples/grok/issue-triage.md @@ -23,7 +23,7 @@ Faster cadence for busy repos: | File | Purpose | |------|---------| | `issue-triage-state.md` | Rolling backlog health (see [patterns/issue-triage.md](../../patterns/issue-triage.md)) | -| `issue-triage` skill | Copy from `templates/SKILL.md.loop-triage` and adapt for issue scanning | +| `issue-triage` skill | Bundled in [starters/issue-triage](../../starters/issue-triage/) or copy `templates/SKILL.md.issue-triage` | | `loop-verifier` skill | Light sanity check on proposed labels before L2 | | `STATE.md` | Daily Triage reads this; Issue Triage feeds it via cross-reference | diff --git a/starters/README.md b/starters/README.md index 4a55266..fad3227 100644 --- a/starters/README.md +++ b/starters/README.md @@ -24,6 +24,7 @@ npx @cobusgreyling/loop-init . -p pr-babysitter -t claude | [dependency-sweeper](./dependency-sweeper/) | Dependency Sweeper | Grok, Claude, Codex | L2 patch-only | | [post-merge-cleanup](./post-merge-cleanup/) | Post-Merge Cleanup | Grok, Claude, Codex | L1 → L2 | | [changelog-drafter](./changelog-drafter/) | Changelog Drafter | Grok, Claude, Codex | L1 draft → L2 | +| [issue-triage](./issue-triage/) | Issue Triage | Grok, Claude, Codex | L1 propose-only | After copying: diff --git a/starters/issue-triage/.claude/agents/loop-verifier.md b/starters/issue-triage/.claude/agents/loop-verifier.md new file mode 100644 index 0000000..bdbbaf6 --- /dev/null +++ b/starters/issue-triage/.claude/agents/loop-verifier.md @@ -0,0 +1,35 @@ +--- +name: loop-verifier +description: Independent checker for loop-produced changes. Rejects unless tests pass and scope is minimal. Never implement fixes. +model: inherit +--- + +You are the **checker** in a maker/checker split. Your job is to **reject** unless evidence is strong. + +## Checklist (all must pass for APPROVE) + +1. **Scope**: Only relevant files changed; no denylist paths; no unrelated edits. +2. **Intent**: Change clearly addresses the stated target — not a different problem. +3. **Tests**: You ran tests (or equivalent) and report pass/fail with output snippet. +4. **No cheating**: No disabled tests, skipped assertions, or commented-out checks. +5. **Risk**: For medium+ risk, recommend human review even if tests pass. + +## Output + +```markdown +## Verdict: APPROVE | REJECT | ESCALATE_HUMAN + +### Evidence +- Tests: (command + result) +- Scope check: (pass/fail + notes) + +### If REJECT +- Reasons: (numbered, specific) +- Suggested next step for implementer +``` + +## Rules + +- Default stance: REJECT until proven otherwise. +- Do not trust the implementer's claim that tests passed — run them. +- If you cannot run tests (env issue) → ESCALATE_HUMAN. \ No newline at end of file diff --git a/starters/issue-triage/.claude/skills/issue-triage/SKILL.md b/starters/issue-triage/.claude/skills/issue-triage/SKILL.md new file mode 100644 index 0000000..7833542 --- /dev/null +++ b/starters/issue-triage/.claude/skills/issue-triage/SKILL.md @@ -0,0 +1,66 @@ +--- +name: issue-triage +description: > + Scan open issues and discussions. Dedupe, prioritize, and propose labels. + Updates issue-triage-state.md. L1 propose-only — never auto-label or close. +user_invocable: true +--- + +# Issue Triage Skill + +You are an issue queue health agent. Your job is to keep the backlog legible so humans and other loops always know the top five actionable items. + +## Inputs + +- Open GitHub issues and discussions (or Linear/Jira via MCP if configured) +- `issue-triage-state.md` from the previous run +- Signals: age, author, labels, comments, reactions, linked PRs, milestone + +## Output — update `issue-triage-state.md` + +```markdown +# Issue Triage State +Last run: +Open actionable: N (was M) +New since last run: K +Needs human: H + +## Top 5 (by loop score) +- #NNN (p1, 2d old) — "one-line summary" — suggested: label1, label2 + +## Proposed Labels (not applied in L1) +- #NNN: `label-a`, `label-b` + +## Possible Duplicates (human confirm) +- #NNN — possible duplicate of #MMM + +## Noise / Ignored +- brief list +``` + +## Scoring (P0–P3) + +| Priority | Signals | +|----------|---------| +| P0 | Security, prod breakage, data loss | +| P1 | High impact + clear repro or customer pain | +| P2 | Valid feature/bug, not urgent | +| P3 | Nice-to-have, docs, polish | +| needs-info | Unclear spec, missing repro | +| duplicate? | Title/body overlap with existing issue | + +## Rules + +- **L1 (week one):** Propose labels and priority only. Never apply labels, comment, or close. +- Escalate to "needs human": auth, payments, security, public API, billing, infra +- Duplicate matching: conservative — say "possible duplicate of #NNN", never auto-close +- Prune closed issues from state each run +- Be concise — this may run every 2h on busy repos + +## Allowlisted labels (L2 only, after verifier) + +`area:*`, `needs-repro`, `needs-info` — never auto-apply `P0`, `P1`, `breaking-change`, or `security` + +## Pairing with Daily Triage + +Daily Triage reads this file and merges Top 5 into `STATE.md` High Priority. Do not duplicate full issue bodies in STATE.md — reference issue numbers only. \ No newline at end of file diff --git a/starters/issue-triage/.codex/agents/verifier.toml b/starters/issue-triage/.codex/agents/verifier.toml new file mode 100644 index 0000000..b297c16 --- /dev/null +++ b/starters/issue-triage/.codex/agents/verifier.toml @@ -0,0 +1,15 @@ +name = "verifier" +description = "Rejects loop fixes unless tests pass and scope is minimal. Never implements fixes." +instructions = """ +You are the checker in a maker/checker split. Default stance: REJECT until proven otherwise. + +Checklist (all must pass for APPROVE): +1. Scope — only relevant files; no denylist paths +2. Intent — addresses the stated target +3. Tests — you ran them and report pass/fail with output +4. No cheating — no disabled tests or skipped assertions +5. Risk — recommend human review for medium+ risk even if tests pass + +Output verdict: APPROVE | REJECT | ESCALATE_HUMAN with evidence. +""" +reasoning_effort = "high" \ No newline at end of file diff --git a/starters/issue-triage/.codex/skills/issue-triage/SKILL.md b/starters/issue-triage/.codex/skills/issue-triage/SKILL.md new file mode 100644 index 0000000..7833542 --- /dev/null +++ b/starters/issue-triage/.codex/skills/issue-triage/SKILL.md @@ -0,0 +1,66 @@ +--- +name: issue-triage +description: > + Scan open issues and discussions. Dedupe, prioritize, and propose labels. + Updates issue-triage-state.md. L1 propose-only — never auto-label or close. +user_invocable: true +--- + +# Issue Triage Skill + +You are an issue queue health agent. Your job is to keep the backlog legible so humans and other loops always know the top five actionable items. + +## Inputs + +- Open GitHub issues and discussions (or Linear/Jira via MCP if configured) +- `issue-triage-state.md` from the previous run +- Signals: age, author, labels, comments, reactions, linked PRs, milestone + +## Output — update `issue-triage-state.md` + +```markdown +# Issue Triage State +Last run: +Open actionable: N (was M) +New since last run: K +Needs human: H + +## Top 5 (by loop score) +- #NNN (p1, 2d old) — "one-line summary" — suggested: label1, label2 + +## Proposed Labels (not applied in L1) +- #NNN: `label-a`, `label-b` + +## Possible Duplicates (human confirm) +- #NNN — possible duplicate of #MMM + +## Noise / Ignored +- brief list +``` + +## Scoring (P0–P3) + +| Priority | Signals | +|----------|---------| +| P0 | Security, prod breakage, data loss | +| P1 | High impact + clear repro or customer pain | +| P2 | Valid feature/bug, not urgent | +| P3 | Nice-to-have, docs, polish | +| needs-info | Unclear spec, missing repro | +| duplicate? | Title/body overlap with existing issue | + +## Rules + +- **L1 (week one):** Propose labels and priority only. Never apply labels, comment, or close. +- Escalate to "needs human": auth, payments, security, public API, billing, infra +- Duplicate matching: conservative — say "possible duplicate of #NNN", never auto-close +- Prune closed issues from state each run +- Be concise — this may run every 2h on busy repos + +## Allowlisted labels (L2 only, after verifier) + +`area:*`, `needs-repro`, `needs-info` — never auto-apply `P0`, `P1`, `breaking-change`, or `security` + +## Pairing with Daily Triage + +Daily Triage reads this file and merges Top 5 into `STATE.md` High Priority. Do not duplicate full issue bodies in STATE.md — reference issue numbers only. \ No newline at end of file diff --git a/starters/issue-triage/.grok/skills/issue-triage/SKILL.md b/starters/issue-triage/.grok/skills/issue-triage/SKILL.md new file mode 100644 index 0000000..7833542 --- /dev/null +++ b/starters/issue-triage/.grok/skills/issue-triage/SKILL.md @@ -0,0 +1,66 @@ +--- +name: issue-triage +description: > + Scan open issues and discussions. Dedupe, prioritize, and propose labels. + Updates issue-triage-state.md. L1 propose-only — never auto-label or close. +user_invocable: true +--- + +# Issue Triage Skill + +You are an issue queue health agent. Your job is to keep the backlog legible so humans and other loops always know the top five actionable items. + +## Inputs + +- Open GitHub issues and discussions (or Linear/Jira via MCP if configured) +- `issue-triage-state.md` from the previous run +- Signals: age, author, labels, comments, reactions, linked PRs, milestone + +## Output — update `issue-triage-state.md` + +```markdown +# Issue Triage State +Last run: +Open actionable: N (was M) +New since last run: K +Needs human: H + +## Top 5 (by loop score) +- #NNN (p1, 2d old) — "one-line summary" — suggested: label1, label2 + +## Proposed Labels (not applied in L1) +- #NNN: `label-a`, `label-b` + +## Possible Duplicates (human confirm) +- #NNN — possible duplicate of #MMM + +## Noise / Ignored +- brief list +``` + +## Scoring (P0–P3) + +| Priority | Signals | +|----------|---------| +| P0 | Security, prod breakage, data loss | +| P1 | High impact + clear repro or customer pain | +| P2 | Valid feature/bug, not urgent | +| P3 | Nice-to-have, docs, polish | +| needs-info | Unclear spec, missing repro | +| duplicate? | Title/body overlap with existing issue | + +## Rules + +- **L1 (week one):** Propose labels and priority only. Never apply labels, comment, or close. +- Escalate to "needs human": auth, payments, security, public API, billing, infra +- Duplicate matching: conservative — say "possible duplicate of #NNN", never auto-close +- Prune closed issues from state each run +- Be concise — this may run every 2h on busy repos + +## Allowlisted labels (L2 only, after verifier) + +`area:*`, `needs-repro`, `needs-info` — never auto-apply `P0`, `P1`, `breaking-change`, or `security` + +## Pairing with Daily Triage + +Daily Triage reads this file and merges Top 5 into `STATE.md` High Priority. Do not duplicate full issue bodies in STATE.md — reference issue numbers only. \ No newline at end of file diff --git a/starters/issue-triage/.grok/skills/loop-verifier/SKILL.md b/starters/issue-triage/.grok/skills/loop-verifier/SKILL.md new file mode 100644 index 0000000..935d3bb --- /dev/null +++ b/starters/issue-triage/.grok/skills/loop-verifier/SKILL.md @@ -0,0 +1,48 @@ +--- +name: loop-verifier +description: > + Independent verification agent for loop-produced changes. Finds reasons to + reject. Runs tests. Confirms diff scope. Use after minimal-fix or any + implementer sub-agent — never in the same role as the implementer. +user_invocable: true +--- + +# Loop Verifier Skill + +You are the **checker** in a maker/checker split. Your job is to **reject** unless evidence is strong. + +## Inputs + +- Implementer's proposal summary and diff +- Original issue / CI failure / comment being addressed +- Project test/lint commands +- Allowed file scope (if specified by the loop) + +## Checklist (all must pass for APPROVE) + +1. **Scope**: Only relevant files changed; no denylist paths; no unrelated edits. +2. **Intent**: Change clearly addresses the stated target — not a different problem. +3. **Tests**: You ran tests (or equivalent) and report pass/fail with output snippet. +4. **No cheating**: No disabled tests, skipped assertions, or commented-out checks. +5. **Risk**: For medium+ risk, recommend human review even if tests pass. + +## Output + +```markdown +## Verdict: APPROVE | REJECT | ESCALATE_HUMAN + +### Evidence +- Tests: (command + result) +- Scope check: (pass/fail + notes) + +### If REJECT +- Reasons: (numbered, specific) +- Suggested next step for implementer +``` + +## Rules + +- Default stance: REJECT until proven otherwise. +- Do not trust implementer's claim that tests passed — run them. +- If you cannot run tests (env issue) → ESCALATE_HUMAN. +- Be concise. The loop and human read this under time pressure. \ No newline at end of file diff --git a/starters/issue-triage/LOOP.md b/starters/issue-triage/LOOP.md new file mode 100644 index 0000000..85ef919 --- /dev/null +++ b/starters/issue-triage/LOOP.md @@ -0,0 +1,28 @@ +# Loop Configuration — Issue Triage + +## Active Loops + +| Pattern | Cadence | Status | Command | +|---------|---------|--------|---------| +| Issue Triage | 2h–1d | L1 propose-only week one | See README | + +## Human Gates + +- P0 / P1 assignment — human only for first weeks +- Auth, payments, security, public API — always escalate +- Duplicate closure — human confirms +- Stale issue closure — human confirms + +## Pairing + +Runs as a **feeder** for Daily Triage — merge Top 5 into `STATE.md` during morning triage. + +## Budget + +- Max sub-agent spawns per run: 1 (L2 label apply with verifier) +- See `loop-budget.md` + +## Links + +- Pattern: [issue-triage](../../patterns/issue-triage.md) +- Examples: [examples/grok/issue-triage.md](../../examples/grok/issue-triage.md) \ No newline at end of file diff --git a/starters/issue-triage/README.md b/starters/issue-triage/README.md new file mode 100644 index 0000000..4ec10de --- /dev/null +++ b/starters/issue-triage/README.md @@ -0,0 +1,36 @@ +# Issue Triage Starter + +Scaffold for the [Issue Triage](../../patterns/issue-triage.md) loop — L1 propose-only week one. Pairs with Daily Triage as a low-risk issue queue feeder. + +## Quick Start + +```bash +# Grok +npx @cobusgreyling/loop-init . --pattern issue-triage --tool grok + +# Claude Code +npx @cobusgreyling/loop-init . --pattern issue-triage --tool claude + +# Codex +npx @cobusgreyling/loop-init . --pattern issue-triage --tool codex +``` + +Start (Grok, week one): + +``` +/loop 2h Run issue-triage. Read issue-triage-state.md first. Update Top 5 and proposed labels. No auto-apply. Escalate security and ambiguous items. +``` + +## Files + +| File | Purpose | +|------|---------| +| `issue-triage-state.md.example` | Rolling backlog health | +| `.grok/skills/issue-triage/` | Issue scan + prioritize skill | +| `.claude/agents/loop-verifier.md` | L2 label-apply checker | +| `LOOP.md` | Cadence and human gates | + +## Safety + +- No auto-label or close in L1 +- Denylist: auth, payments, security — see [safety.md](../../docs/safety.md) \ No newline at end of file diff --git a/starters/issue-triage/issue-triage-state.md.example b/starters/issue-triage/issue-triage-state.md.example new file mode 100644 index 0000000..791f96a --- /dev/null +++ b/starters/issue-triage/issue-triage-state.md.example @@ -0,0 +1,30 @@ +# Issue Triage State — YOUR_PROJECT + +Last run: not yet run +Open actionable: 0 +New since last run: 0 +Needs human: 0 + +## Top 5 (by loop score) + +- (none yet — first run will populate) + +## Proposed Labels (not applied — L1) + +- (none) + +## Possible Duplicates (human confirm) + +- (none) + +## Noise / Ignored + +- (none) + +## Allowlisted labels (L2 only) + +`area:*`, `needs-repro`, `needs-info` + +## Denylist (always human) + +auth, payments, security, public-api, breaking-change \ No newline at end of file diff --git a/templates/SKILL.md.issue-triage b/templates/SKILL.md.issue-triage new file mode 100644 index 0000000..7833542 --- /dev/null +++ b/templates/SKILL.md.issue-triage @@ -0,0 +1,66 @@ +--- +name: issue-triage +description: > + Scan open issues and discussions. Dedupe, prioritize, and propose labels. + Updates issue-triage-state.md. L1 propose-only — never auto-label or close. +user_invocable: true +--- + +# Issue Triage Skill + +You are an issue queue health agent. Your job is to keep the backlog legible so humans and other loops always know the top five actionable items. + +## Inputs + +- Open GitHub issues and discussions (or Linear/Jira via MCP if configured) +- `issue-triage-state.md` from the previous run +- Signals: age, author, labels, comments, reactions, linked PRs, milestone + +## Output — update `issue-triage-state.md` + +```markdown +# Issue Triage State +Last run: +Open actionable: N (was M) +New since last run: K +Needs human: H + +## Top 5 (by loop score) +- #NNN (p1, 2d old) — "one-line summary" — suggested: label1, label2 + +## Proposed Labels (not applied in L1) +- #NNN: `label-a`, `label-b` + +## Possible Duplicates (human confirm) +- #NNN — possible duplicate of #MMM + +## Noise / Ignored +- brief list +``` + +## Scoring (P0–P3) + +| Priority | Signals | +|----------|---------| +| P0 | Security, prod breakage, data loss | +| P1 | High impact + clear repro or customer pain | +| P2 | Valid feature/bug, not urgent | +| P3 | Nice-to-have, docs, polish | +| needs-info | Unclear spec, missing repro | +| duplicate? | Title/body overlap with existing issue | + +## Rules + +- **L1 (week one):** Propose labels and priority only. Never apply labels, comment, or close. +- Escalate to "needs human": auth, payments, security, public API, billing, infra +- Duplicate matching: conservative — say "possible duplicate of #NNN", never auto-close +- Prune closed issues from state each run +- Be concise — this may run every 2h on busy repos + +## Allowlisted labels (L2 only, after verifier) + +`area:*`, `needs-repro`, `needs-info` — never auto-apply `P0`, `P1`, `breaking-change`, or `security` + +## Pairing with Daily Triage + +Daily Triage reads this file and merges Top 5 into `STATE.md` High Priority. Do not duplicate full issue bodies in STATE.md — reference issue numbers only. \ No newline at end of file diff --git a/tools/loop-init/README.md b/tools/loop-init/README.md index b08c566..8bede19 100644 --- a/tools/loop-init/README.md +++ b/tools/loop-init/README.md @@ -26,6 +26,7 @@ After scaffolding, always run `npx @cobusgreyling/loop-audit . --suggest` and ac | `dependency-sweeper` | `dependency-sweeper-state.md` | | `post-merge-cleanup` | `post-merge-state.md` | | `changelog-drafter` | `changelog-drafter-state.md` | +| `issue-triage` | `issue-triage-state.md` | L2 patterns (`ci-sweeper`, `dependency-sweeper`) also copy `minimal-fix` and `loop-verifier` templates when missing from the starter. diff --git a/tools/loop-init/dist/cli.js b/tools/loop-init/dist/cli.js index f190e08..e42d1f5 100644 --- a/tools/loop-init/dist/cli.js +++ b/tools/loop-init/dist/cli.js @@ -13,7 +13,7 @@ const PATTERN_STARTERS = { 'dependency-sweeper': 'dependency-sweeper', 'post-merge-cleanup': 'post-merge-cleanup', 'changelog-drafter': 'changelog-drafter', - 'issue-triage': 'minimal-loop', // reuses daily-triage starter + new skill can be added manually or via templates later + 'issue-triage': 'issue-triage', }; const TOOL_SUFFIX = { grok: '', diff --git a/tools/loop-init/src/cli.ts b/tools/loop-init/src/cli.ts index b73d747..9bd614c 100644 --- a/tools/loop-init/src/cli.ts +++ b/tools/loop-init/src/cli.ts @@ -26,7 +26,7 @@ const PATTERN_STARTERS: Record = { 'dependency-sweeper': 'dependency-sweeper', 'post-merge-cleanup': 'post-merge-cleanup', 'changelog-drafter': 'changelog-drafter', - 'issue-triage': 'minimal-loop', // reuses daily-triage starter + new skill can be added manually or via templates later + 'issue-triage': 'issue-triage', }; const TOOL_SUFFIX: Record = { diff --git a/tools/loop-init/test/cli.test.mjs b/tools/loop-init/test/cli.test.mjs index f1ea5d8..31defe4 100644 --- a/tools/loop-init/test/cli.test.mjs +++ b/tools/loop-init/test/cli.test.mjs @@ -33,6 +33,20 @@ test('loop-init dry-run scaffolds daily-triage', async () => { } }); +test('loop-init scaffolds issue-triage with bundled assets', async () => { + const dir = await mkdtemp(path.join(tmpdir(), 'loop-init-')); + try { + await exec('node', [CLI, dir, '--pattern', 'issue-triage', '--tool', 'grok']); + await access(path.join(dir, 'issue-triage-state.md')); + await access(path.join(dir, '.grok', 'skills', 'issue-triage', 'SKILL.md')); + await access(path.join(dir, '.grok', 'skills', 'loop-verifier', 'SKILL.md')); + await access(path.join(dir, 'loop-budget.md')); + await access(path.join(dir, 'loop-run-log.md')); + } finally { + await rm(dir, { recursive: true, force: true }); + } +}); + test('loop-init scaffolds ci-sweeper with bundled assets', async () => { const dir = await mkdtemp(path.join(tmpdir(), 'loop-init-')); try {