debt-triage · v1.0.0 · 2026-10-17 · sha256 3b87cd484169ead1

debt-triage v1.0.0A

Immutable. This exact content never changes and is served at /api/v1/blob/3b87cd484169ead1.

---
name: debt-triage
description: EM-PM ceremony to prioritize the debt backlog.
version: 1.0.0
allowed-tools: ["Read","Write","Edit","Bash","Grep","Glob","Agent","Skill","AskUserQuestion","TaskCreate","TaskUpdate","TaskGet","TaskList"]
---

<!-- 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:
1. Close resolved items — stamp `status: closed`, `closed_at:`, `closed_by: <sha>`, then
   `mkdir -p archive/debt-backlog/<YYYY-MM>` and `git mv` the entry in. Never `rmdir
   state/debt-backlog/` even if it empties.
2. Update `severity` per PM direction.
3. Remove YAGNI items the same way as (1), `closed_by` referencing the PM decision.
4. 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`.
5. 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 per `coordinator/docs/wiki/ceremony-calibration/queue-terminus-doctrine.md` § Clustering, then mint
  solo or themed batons to `coordinator/docs/wiki/baton-lifecycle/baton-authoring-bar.md`'s bar with
  triage's sizing evidence; no second gate. Stamp `initiative` on a themed baton and its member
  rows. Scaffold with `coordinator-doc-new`, set `category` to `queue-derived-baton` if left
  `infra`. Close each source row.
- `route-to-learn-lessons`: run `coordinator-lesson-promote` once per row with `--title-file` and
  `--body-file` (the row's title and body), `--change-kind` (the row's), `--target-wiki unknown`,
  and `--evidence` naming 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 with `closed_by` set
  to the settling commit sha (the outbox path goes in the commit message, not `closed_by`).
  - Promotes only rows later runs hand back. The `reconcile-343` plan promotes the existing
    central backlog once (C1 classification, C4 execution), so this promote gates on that
    classification's output or landed C4, never on the `queue_scope: central` tag (evidence, not
    disposition). Until then a `route-to-learn-lessons` hand-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.
- 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-revert` follows coordinator-content-repo-47's `/bug-blitz` post-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:

1. Act on a ruling only where this skill already permits EM-autonomous action (closing a
   verified-resolved row, Step 2's `cross-repo` memo-then-close, re-prioritizing).
2. A `pm_only` matter gets `apm_recommendation` on the row and stays open.
3. 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.