vt-d-triage-bugs¶
Triage pending bug reports — promote to bugs/, defer, or mark as duplicate
Plugin: vt-product-dev
Category: Other
Command: /vt-d-triage-bugs
/vt-d-triage-bugs — Bug Triage Skill¶
Process pending bug reports in intake/pending/bugs/. Bulk-archive any whose status is already terminal (resolved/fixed/duplicate/wontfix), then for each remaining open bug decide: promote to the tracked bugs backlog, defer for later, or close as duplicate.
Invocation¶
Execution¶
Step 1: Scan Pending Bugs¶
Glob intake/pending/bugs/BUG-*.md.
For each file, read YAML frontmatter and partition by status::
status: value |
Handling |
|---|---|
deferred |
Skip silently — already triaged as "later". |
resolved, fixed, duplicate, wontfix |
Terminal. Do not offer Promote/Defer/Duplicate — the work is finished. Collect these and archive them in bulk (Step 1b). |
new, reported, pending, triaged, or absent |
Triage normally (Step 2). |
| anything else | Triage normally, but print ⚠ unknown status "{value}" in BUG-NNN — add it to the table in Step 1. Never silently treat an unrecognised status as open. |
Why this partition exists. A bug fixed directly — without ever being promoted to
bugs/NNN-slug/— keeps its intake file withstatus: resolved. Nothing retires it, so it counts as open work in every triage run and every dashboard. A 2026-08-12 triage found 7 of 15 pending bugs already resolved this way. Offering Promote/Defer/Duplicate for those forces a wrong answer: Promote creates a backlog entry to investigate solved work, and Duplicate is false.
If no files at all:
Exit.Step 1b: Archive Terminal Bugs¶
If the partition above found any terminal-status bugs, list them and confirm once for the whole set (not per bug) via AskUserQuestion:
- Archive all — for each:
mkdir -p intake/processed/bugs/, thengit mv intake/pending/bugs/BUG-NNN.md intake/processed/bugs/BUG-NNN.md. Preserve the existing status — do not rewriteresolvedtotriaged; the archived record must stay truthful. - Leave pending — change nothing; they will resurface on the next run.
Report per file: ⤓ BUG-NNN archived (status: resolved).
If a terminal bug does have a tracked bugs/NNN-*/ directory, archive it the same way — the
tracked directory is the live record and the intake file is the duplicate.
Then continue to Step 2 with the normal-triage set only.
Step 2: Process Each Bug¶
For each pending bug (sorted by filename):
Display summary:
───────────────────────────────────────────────────────
BUG-NNN: [title]
Severity: [severity]
Reported: [reported_at]
───────────────────────────────────────────────────────
Steps: [steps to reproduce, truncated to 3 lines]
Expected: [expected behavior]
Actual: [actual behavior]
───────────────────────────────────────────────────────
Ask via AskUserQuestion: "What should we do with BUG-NNN?" Options: - Promote — move to bugs/ backlog for investigation - Defer — keep in intake, mark deferred (skip in future triage) - Duplicate — close as duplicate of an existing bug
If Promote:¶
- ID-collision guard — run this BEFORE creating anything. Check whether the ID is already
taken by a different bug:
If a directory exists, compare its
report.mdH1 against this bug's title. If they describe different bugs, STOP and ask the user via AskUserQuestion — do notmkdira secondbugs/${NNN}-*directory, and do not write abugchen_status: BUG-NNNentry, which would overwrite the existing bug's record.
When renumbering, the next free ID is the max across every branch, not just the current worktree:
{ git ls-tree -r --name-only --full-tree $(git for-each-ref --format='%(refname)' refs/heads/) \
-- bugs/ intake/pending/bugs/ intake/processed/bugs/ 2>/dev/null
ls -d bugs/*/ intake/*/bugs/* 2>/dev/null
} | grep -oE 'bugs/[0-9]{3}-|BUG-[0-9]{3}' | grep -oE '[0-9]{3}' | sort -n | tail -1
bugs/[0-9]{3}- and BUG-[0-9]{3} anchors are load-bearing. A bare [0-9]{3} also
matches the 202 in date-named files such as
intake/processed/bugs/2026-05-18-pre-commit-hook-allow-merge-commits.md, which floors the max
to 202 and hands out an absurd ID. Requiring the trailing - after three digits rejects
bugs/2026-… while still matching bugs/063-…. This is the same defect that poisoned
allocate_spec_id.py (it returned SPEC-2031 against a real max of 170); do not "simplify" the
pattern.
Allocating the lowest ID free on main is exactly how the collision recurs: an unmerged branch
may already hold that ID and will collide when it merges. A real 2026-08-12 case —
intake/pending/bugs/BUG-050.md and bugs/050-install-exits-zero-without-registering/ were
unrelated bugs sharing an ID; main's max was 056 but feature/spec-170 already held 057–062,
so the safe allocation was 063, not 057.
This is a workaround, not a fix. The root cause is BUG-047 — bug IDs have no allocator, so parallel sessions collide. Until that lands, every promote is a chance to collide.
- Build slug from title: lowercase, spaces → hyphens, max 30 chars, strip special chars
Example: "Login fails on Safari" →
login-fails-on-safari - Run
mkdir -p bugs/NNN-slug/(NNN = zero-padded fromid:field) - Copy intake file content →
bugs/NNN-slug/report.md - Resolve the bug's domain (SPEC-122):
- Read
domain:from the source frontmatter - If present, preserve as-is in
bugs/NNN-slug/report.md(the copy in step 3 already carries it; no edit needed) - If absent (legacy bug authored before SPEC-122), prompt the user via
AskUserQuestion. Build the option list dynamically from
intake/projects.yaml(uniquedomain:values acrossprojects:entries), and appendcross-cuttingas a fixed final choice - Write the chosen value into the
domain:frontmatter field ofbugs/NNN-slug/report.md(and into the intake copy at step 7 below so the archived record stays consistent) - Write
bugs/NNN-slug/state.yaml: - Update
.design-state.yaml: add entry underbugchen_status::Ifbugchen_status: BUG-NNN: dir: bugs/NNN-slug/ title: [title] severity: [severity] status: triaged domain: [domain from step 4] reported_at: [reported_at from source frontmatter]bugchen_status:key does not exist in.design-state.yaml, add it. Thestatus:,domain:, andreported_at:fields are required by SPEC-122parse_bugchen_statusfor dashboard filtering and rendering (matches data-model.md Entity 3 schema). - Update intake file frontmatter: set
status: triaged(so the archived copy reflects final disposition). Also write the resolveddomain:value from step 4 if the intake file lacked it. - Ensure
intake/processed/bugs/directory exists (runmkdir -p intake/processed/bugs/if needed) - Run
git mv intake/pending/bugs/BUG-NNN.md intake/processed/bugs/BUG-NNN.md - Report:
✓ BUG-NNN promoted to bugs/NNN-slug/
If Defer:¶
- Edit
intake/pending/bugs/BUG-NNN.mdfrontmatter: setstatus: deferred - Report:
→ BUG-NNN deferred
If Duplicate:¶
- Ask via AskUserQuestion: "Duplicate of which bug? (e.g. BUG-005)"
- Edit intake file frontmatter: set
status: duplicate, addduplicate_of: [answer] - Ensure
intake/processed/bugs/directory exists (runmkdir -p intake/processed/bugs/if needed) - Run
git mv intake/pending/bugs/BUG-NNN.md intake/processed/bugs/BUG-NNN.md - Report:
✗ BUG-NNN closed as duplicate of [answer]
Step 3: Summary¶
After all bugs are processed, display:
Triage complete
───────────────────────────────────────────────────────
Archived: N (already resolved → intake/processed/bugs/)
Promoted: N (ready for /vt-d-investigate-bug)
Deferred: N (will skip on next triage)
Duplicate: N (moved to intake/processed/bugs/)
Renumbered: N (ID collision — list old → new)
───────────────────────────────────────────────────────
Promoted bugs ready for investigation:
BUG-NNN [title] → bugs/NNN-slug/
...
Next: /vt-d-investigate-bug BUG-NNN to investigate a promoted bug