debt-triage
EM-PM ceremony to prioritize the debt backlog.
SKILL.mdskills/debt-triage/SKILL.md
GitHub<!-- Schema: state/debt-backlog/*.yaml (YAML per entry); closure via git mv to archive/debt-backlog/<YYYY-MM>/. -->
Debt Triage — Backlog Review and Prioritization
Announce at start: "I'm using the coordinator:debt-triage skill to review the debt backlog."
An EM-PM conversation, not a dispatched agent: the EM reads the backlog and recommends. Trigger on demand, at >20 open items, or after a refactor that may have resolved several. Rationale, clustering detail, structural-probe calibration: wiki.
On a PowerShell host every CLI below takes its .exe launcher through the call operator (Shape W),
never the ${...} POSIX form. Ladder: ${CLAUDE_PLUGIN_ROOT}/snippets/resolve-coordinator-bin.md.
Run backlog-grind-assemble brief debt-triage (per ${CLAUDE_PLUGIN_ROOT}/snippets/resolve-coordinator-bin.md)
before Step 1. Over state/debt-backlog/, state/bug-backlog/ and state/improvement-queue/ it
returns open items with severity breakdown, bug-backlog cross-reference (exact surface-field
match), improvement-queue entries with clustering evidence, and a batched PM-gate. The improvement
leg is emitted (Step 1); the debt-backlog steps (2, 3, 6, and 6b's debt-backlog governance) stay EM-performed.
Step 0: Surface prior rejections
Check tasks/out-of-scope/*.md (skip silently if absent). For any concept overlapping the
triage, surface: "This is similar to tasks/out-of-scope/<concept>.md — we rejected this
because [reason]. Still feel the same?" — confirm, reconsider (delete the file), or override.
Step 1: Read current state
Take the brief output as-is. Overlap beyond the surface-field match (file-path/description
similarity) is an EM judgment pass over the same evidence, before presenting overlaps to the PM for
a dedup decision (populate evidence: on both entries).
Improvement-queue triage is emitted, not EM classification. Pick an appetite (hunt,
standard or sweep — values in coordinator/queue-profiles/improvement.yaml). Emit with
emit-dispatch-workflow --queue state/improvement-queue --profile improvement --appetite <a> --out state/scratch/debt-triage/{run-id}/improvement.workflow.mjs --repo-root <abs repo root>, per
${CLAUDE_PLUGIN_ROOT}/snippets/resolve-coordinator-bin.md. There is no commit-readiness gate
to resolve for this leg. Fire with Workflow({scriptPath}), never --fire — firing authorizes
the in-run fixes and closes and the post-run hand-back (coordinator/docs/wiki/ceremony-calibration/queue-terminus-doctrine.md
§ Emitted-workflow triage). The run's triage is the only triage; the EM works Steps 2–4 on the
debt backlog meanwhile. An emit refusal is reported, not routed around. The grind commits: a
/bug-blitz in the same tree takes its Phase 0.7 suite baseline before this grind fires or after it
finishes, never across it.
Step 2: Verify relevance (emitted)
Do not ask whether to fire — invoking this skill IS the request for the run this step names; it dissolves no gate this skill's own body names.
Debt-backlog rows only, emitted as in Step 1: emit-dispatch-workflow --queue state/debt-backlog --profile debt --appetite <a> --out state/scratch/debt-triage/{run-id}/debt.workflow.mjs --repo-root <abs repo root>, fired with Workflow({scriptPath}). Never hand-dispatch verifiers.
Relevance and evidence rules live in coordinator/queue-profiles/debt.yaml (relevance_first,
defer_requires_evidence). A row whose cited surface moved to another repo comes back as
cross-repo: route it by cross-repo memo, then close it here once delivered. Code that moved is
not code that was fixed. Hand-back types feed Step 5 as the improvement leg's do.
Step 3: Re-prioritize
Blocking other work → P0. In a D/F-graded system → P1. In a recently A/B-graded system → may deprioritize to P2. >30 days with no activity → flag for PM attention (skill policy over entry frontmatter dates; the PM-altitude call is the point).
Before grouping, query historical nature: tech-debt completions
(query-completions --where "nature=tech-debt" --since "90d" --format json, ranked descending on
frontmatter.loe.agent_dispatches as a number — the reader takes no --sort —
per ${CLAUDE_PLUGIN_ROOT}/snippets/resolve-coordinator-bin.md): high-LoE areas in the last 90d
indicate festering complexity — escalate open items there.
Never rank on loe.tshirt. As strings, descending order is XXL, XS, XL, S, M, L — the
smallest tier ranks second. Rank on a numeric field.
Zero-activity areas may reflect avoidance — flag: "We have carried this debt for N days without
touching it — is that intentional?" Present a one-paragraph summary before Step 4; zero-row
case: (no tech-debt completions logged in last 90d — hot-zone analysis unavailable).
Step 4: Group for execution
"System" is the brief's debt-backlog groups evidence: the first path cited in surface,
else in title/body, cut to two directories. The row's system field is free text and often
absent — never group on it.
Present ## Triage Results with four ### tables, each headed <label>: N items:
Closed (no longer applicable) (ID, Reason); Recommended for immediate action (ID, System,
Severity, Description, Effort); Can defer (ID, System, Severity, Reason to defer); Needs PM
decision (YAGNI/scope) (ID, System, Description, Question).
Step 5: Present to PM
Ask for: (1) approval to close no-longer-applicable items; (2) YAGNI/scope calls; (3)
prioritization of immediate-action items; (4) agreement on deferral reasoning; (5) goes to
coordinator:apm, not the PM: the improvement leg's hand-back types (park, wont-do, yagni,
unclear-direction, needs-judgment) are dispositioned by the APM (a code matter by
coordinator:staff-eng), which writes pm_ruling: "<agent> (PM-delegated) ..." on the row. Step
6b consumes it; the PM sees the receipt. Only a pm_only matter (important, urgent, no clear
answer, or irreversible) reaches the PM. Engine-originated hand-back types (budget-exhausted,
verify-failed, the rest) are reported by count; their rows stay open, not presented here.
Step 6: Update backlog
After PM decisions:
- Close resolved items — stamp
status: closed,closed_at:,closed_by: <sha>, thenmkdir -p archive/debt-backlog/<YYYY-MM>andgit mvthe entry in. Neverrmdir state/debt-backlog/even if it empties. - Update
severityper PM direction. - Remove YAGNI items the same way as (1),
closed_byreferencing the PM decision. - For a load-bearing rejection (scope/doctrine conflict, cost-benefit, architectural veto —
never a bug), write
tasks/out-of-scope/<concept>.md, one file per concept (append "Prior requests" to an existing file rather than duplicating):# Out of scope: <concept>;**First raised:** YYYY-MM-DD;**Status:** Rejected (open to reconsideration); sections## What was proposed,## Why we rejected it,## Prior requests(- YYYY-MM-DD: [how this came up]),## What would change our minds. - Commit scoped, explicit-path:
git commit -m "debt-triage: reviewed N items, closed M, N remain open" -- <every touched path>.
Step 6b: Consume the improvement leg's hand-back
Runs over both emitted legs' APM-dispositioned hand-back (Step 5 item 5), after the runs; a
debt-leg cross-repo hand-back follows Step 2's memo-then-close route.
baton: cluster percoordinator/docs/wiki/ceremony-calibration/queue-terminus-doctrine.md§ Clustering, then mint solo or themed batons tocoordinator/docs/wiki/baton-lifecycle/baton-authoring-bar.md's bar with triage's sizing evidence; no second gate. Stampinitiativeon a themed baton and its member rows. Scaffold withcoordinator-doc-new, setcategorytoqueue-derived-batonif leftinfra. Close each source row.route-to-learn-lessons: runcoordinator-lesson-promoteonce per row with--title-fileand--body-file(the row's title and body),--change-kind(the row's),--target-wiki unknown, and--evidencenaming the archive destination path of the closing rename (or the row stem), never the pre-rename path, stale once archived. Then close the source row withclosed_byset to the settling commit sha (the outbox path goes in the commit message, notclosed_by).- Promotes only rows later runs hand back. The
reconcile-343plan promotes the existing central backlog once (C1 classification, C4 execution), so this promote gates on that classification's output or landed C4, never on thequeue_scope: centraltag (evidence, not disposition). Until then aroute-to-learn-lessonshand-back stays open, untouched and unpromoted — no central-tagged row is promoted here — and Step 5's run summary reports it by count as "awaiting reconcile-343," never closed or presented to the PM. - A row whose change_kind the outbox enum refuses (exit 2) is left open and reported, never coerced.
- Promotes only rows later runs hand back. The
- Park, won't-do and YAGNI keep their current rules and stamps, after Step 5.
- Source-row closure is an edit plus a plain rename to
archive/improvement-queue/<YYYY-MM>/, the committer staging both paths (A-PLAIN-MV-IS-THE-INTENDED-ROUTE-NOT-A-FALLBACK). The row-removal--declared-revertfollows coordinator-content-repo-47's/bug-blitzpost-run wording.
Commit shape: batons, promotes and PM-gated closures are separate commits, each naming the
source ids; the run committed its own fixes and closes. Every hand closure here is followed by
backlog-grind-assemble grind-row sweep (coordinator/docs/wiki/ceremony-calibration/queue-terminus-doctrine.md § Emitted-workflow triage).
Skip this step entirely if no project-specific entries survived Step 5.
Autonomous runs
No PM is present. The APM's ruling is written as in Step 5 — pm_ruling: "apm (PM-delegated) ...",
never as the PM's own word. For each row:
- Act on a ruling only where this skill already permits EM-autonomous action (closing a
verified-resolved row, Step 2's
cross-repomemo-then-close, re-prioritizing). - A
pm_onlymatter getsapm_recommendationon the row and stays open. - Continue; a pending recommendation never stops the run.
The report ends with ONE batched PM-confirmation list: each row with an unconfirmed
apm_recommendation as row id, recommendation, one-line reason. The PM's confirmation writes
pm_ruling (no (PM-delegated) label) for Step 6b.