# Lisa Rework Triage

> Rework detection and agent-failure classification. When a claimed ticket turns out to be rework — previously implemented work bounced back from QA/staging — this skill detects it, classifies WHY the previous agent attempt failed (decomposition infidelity, PRD defect, missing tool access, implementation defect, environment/data, verification gap), posts a structured triage comment, and routes each cause to its hardening destination — including upstream Lisa issues so the harness itself gets fixed and the same mistake structurally cannot repeat. Vendor-neutral: consumes the lisa-tracker-read context bundle; invoked automatically by lisa-ticket-triage Phase 2.5 and runnable standalone.

- Skill: `codyswanngt/lisa-rework-triage` (Agent Skill, multi-file: 2 files)
- Install (CLI): `npx skillmds@latest add codyswanngt/lisa-rework-triage`
- Raw SKILL.md: https://api.skillmd.com/api/skills/codyswanngt/lisa-rework-triage/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Product & Planning
- Author: codyswanngt (https://skillmd.com/u/codyswanngt)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/codyswanngt/lisa-rework-triage

---


# Rework Triage: $ARGUMENTS

Classify why a previous agent attempt at this ticket failed, and convert that cause into a
structural improvement. This is the self-hardening arm of the lifecycle: every classified
failure either hardens the harness (an upstream Lisa issue), the project (a provisioning or
environment ticket), or the spec (a PRD defect flag). A rework cycle that fixes the bug but
never asks *why the agent got it wrong* guarantees the mistake recurs.

The caller MUST have run `lisa-tracker-read` first and provided the context bundle (all
comments chronological, linked PRs with review state, issue links, parent epic). Do not
classify from a bare summary — evidence-free classification is worse than none.

## Phase 1 — Rework detection

A ticket is **rework** when it was previously implemented by this lifecycle and has returned
to a build-ready state. Evaluate these signals in order; the first confirmed signal suffices.
If none confirm, output `## Verdict: NOT_REWORK` and stop — this skill takes no action on
first-attempt work.

1. **Status history shows a post-implementation regression transition.** The ticket's
   changelog contains a transition from a post-build state (the configured `claimed`,
   any configured `done.*` environment state, or a review state) back to the configured
   `ready` state.
   - JIRA: fetch the changelog via the `lisa-atlassian-access` REST substrate —
     `GET /rest/api/3/issue/<KEY>/changelog` — and scan `items[].field == "status"` entries.
   - GitHub: `gh issue view <n> --json timelineItems` (or the timeline API) — scan for the
     `$DONE`/environment label being removed and `$READY` re-added after a linked PR merged.
   - Linear: the Issue history relation — scan state changes from a completed/started state
     back to the ready-labeled state.
2. **Merged linked PR + build-ready again.** The bundle shows at least one linked PR that is
   `MERGED`, while the ticket currently carries the build-ready role. First-attempt tickets
   never have merged PRs.
3. **Prior lifecycle claim marker.** The comments contain a prior implementation-cycle
   marker for THIS ticket: a `[claude-build-intake]` claim comment, or a build-evidence
   comment posted by `lisa-tracker-evidence` (`[lisa-evidence]` / the vendor evidence
   header). Only markers proving a prior *implementation* attempt qualify —
   `[lisa-rework-triage]` comments and other unrelated `[lisa-*]` annotations never do.
4. **Explicit rework marker.** A `rework` or `regression` label applied by QA or the verify
   lifecycle, or a **live** QA-failure signal.

   The QA-failure signal is transient, so its bare presence is not the signal — its
   liveness is. Ask the resolver, never the label list:

   ```bash
   RESOLVER="${CLAUDE_PLUGIN_ROOT:-${PLUGIN_ROOT:-plugins/lisa}}/scripts/qa-signal-lifecycle.mjs"
   node "$RESOLVER" --vendor "<jira|linear|github>" --item bundle.json
   # bundle.json: { "labels": [...], "comments": [<bodies, oldest first>], "role": "<current>" }
   ```

   Branch on `action`, never on the label being there:

   | `outcome` | Meaning | What triage does |
   |---|---|---|
   | `live` | the failure is unresolved | signal 4 fires — this is rework |
   | `stale` | a later QA pass, or the certified/terminal role, voided it | signal 4 does **not** fire; **remove the label** (same tracker surface that applied it) and note the repair in the triage comment. Keep evaluating signals 1–3 |
   | `absent` | no signal | signal 4 does not fire |
   | `unchecked` | a declared void condition has no predicate | treat as `live` and surface it — a control nothing can evaluate is a defect to report, not an exemption |

   Clearing a stale signal here is the repair path for items labelled before the signal had
   an inverse; without it that backlog never drains. The `[lisa-qa-fail]` comments are the
   history and are never touched — a stale signal removed still leaves the failure fully
   readable, and signal 3 still reads those comments.

   `rework` and `regression` are matched on presence as before. Whether they have the same
   shape is **unverified** and not claimed here.

Record which signal fired — it is part of the evidence trail.

## Phase 2 — Failure-cause classification

Classify the failed attempt into **exactly one primary cause**. Every classification must
cite concrete evidence from the bundle, the codebase, or the PRD lineage — a cause without
evidence is `UNCLASSIFIED`, which is itself a surfaced outcome, never a silent default.

| Cause | Definition | Required evidence |
|-------|------------|-------------------|
| `decomposition-infidelity` | The ticket misrepresents the PRD requirement it implements — the agent built what the ticket said, but the ticket distorted the spec. | Resolve the ticket's requirement id(s) from the source PRD's `## Tickets` section (written by `lisa-prd-backlink` — each entry carries `requirements: ["R#"]` and the register maps R# to verbatim PRD text). Quote the PRD text vs. the ticket's AC and name the divergence. |
| `prd-defect` | The ticket faithfully captured the PRD, but the PRD itself was wrong, ambiguous, or missing the failing case. | Quote the PRD requirement and the QA failure it does not cover or contradicts. |
| `missing-tool-access` | The agent lacked a tool, credential, environment, or permission the work required, and worked around it blindly. | The prior cycle's comments/PR show the gap (skipped verification step, "could not access", missing env credentials, absent MCP tool) — cite the marker. |
| `implementation-defect` | Spec and ticket were right; the agent's code was wrong. | The failing behavior contradicts an AC the ticket states correctly; cite the AC and the failure report. |
| `environment-data` | The failure is caused by environment or data state (drift, missing fixtures, shared-state collision), not by the code under test. | The failure reproduces only in the affected environment, or the report references data absent from the environment's seed/fixtures. |
| `verification-gap` | The code was wrong AND the verification lifecycle should have caught it — the codified regression test was missing, too weak, or asserted the wrong thing. | Name the verification/codify-verification artifact from the prior cycle that failed to catch this class of failure. Frequently a **secondary** cause alongside `implementation-defect`; record both, primary first. |

Classification sources, in priority order: the QA failure report (comments), the prior
cycle's evidence comment and PR review thread, the PRD requirement register via
`lisa-prd-backlink` lineage, and the codebase itself (Glob/Grep the failing surface).

## Phase 3 — Post the triage comment

Post one structured comment on the ticket via the vendor comment surface (`lisa-atlassian-access
operation: comment`, `gh issue comment`, or the Linear comment mutation):

```text
[lisa-rework-triage] Rework detected (signal: <signal>)
Fingerprint: <ticket-key>|<last merged PR number, or none>|<primary cause>
Primary cause: <cause>
Secondary cause: <cause or none>
Evidence: <2-4 lines citing the specific PRD text / AC / comment / PR review / environment fact>
Hardening action: <what Phase 4 filed, with link — or "none required (implementation-defect)">
```

**Idempotency:** the fingerprint is the literal `Fingerprint:` line —
`<ticket-key>|<last merged PR number>|<primary cause>`, with `none` as the PR component
when detection fired on a claim marker or label and no merged PR exists. Before posting,
scan existing comments for a `[lisa-rework-triage]` block whose `Fingerprint:` line
matches — if present, skip posting and report `already-triaged`. One triage per bounce,
never one per cycle re-entry.

## Phase 4 — Route the cause to its hardening destination

Each cause has exactly one destination. File through the proper write path — never raw
ticket creation — so every hardening artifact passes the same quality gates as any other
Lisa work item.

| Primary cause | Destination | Action |
|---------------|-------------|--------|
| `decomposition-infidelity` | **Upstream Lisa issue** | The decomposition pipeline produced an unfaithful ticket and every gate passed it — that is a harness defect. File per "Filing upstream" below, citing the PRD text, the distorted ticket AC, and which gate should have caught it. |
| `verification-gap` | **Upstream Lisa issue** | The verification lifecycle passed work it should have failed. File per "Filing upstream", citing the missed failure class and the weak/missing codified test. |
| `missing-tool-access` | Project tracker | Create a provisioning ticket via `lisa-tracker-write` (`issue_type: Task`, labeled `type:tooling`) describing the missing tool/credential/environment and which flow needs it. Link it `blocks` the rework ticket. Pass `human_gate: "a human must grant the missing access"` per `ready-role-filing` — the factory cannot provision its own credentials, so this is a declared gate rather than an unclaimed filing. |
| `environment-data` | Project tracker | Create an environment-hardening ticket via `lisa-tracker-write` citing the drift/fixture/shared-state evidence. Link `relates to` the rework ticket. Pass `build_ready: true` when the hardening is a code/fixture change the factory can make, or `human_gate: "<the shared-environment decision a human owns>"` when it is not — per `ready-role-filing`, never neither. |
| `prd-defect` | Source PRD | Comment on the PRD (via the `lisa-prd-backlink` lineage) quoting the defective requirement and the QA failure; flag for product review. Do NOT silently edit the PRD — spec changes are a human gate. |
| `implementation-defect` (no secondary) | None | Normal fix path; the classification comment is the record. Pass the pattern to the `learner` phase — repeated implementation defects of the same shape escalate to `verification-gap`. |

**Secondary causes route too.** A recorded secondary cause triggers its own row's
destination in addition to the primary's: `implementation-defect` with a
`verification-gap` secondary files the upstream Lisa issue per the `verification-gap`
row — the code being wrong does not excuse the verification lifecycle for passing it.
Deduplicate against the primary's filing (one upstream issue per failure class, not one
per cause slot).
| `UNCLASSIFIED` | Surface to human | Include in the triage comment and the caller's summary; a human decides. Never guess a cause to avoid this outcome. |

### Filing upstream

Upstream issues target the Lisa repository itself so the harness gets fixed for every
project at once. Resolve the repo from `.lisa.config.json` `hardening.upstreamRepo`;
default `CodySwannGT/lisa`.

There are **two upstream lanes**, distinguished by what the filing proposes. Both share
the same dedupe-marker and evidence-chain discipline below; only the label and one body
section differ:

| Lane | Label | When | Extra body section |
|------|-------|------|--------------------|
| Defect | `self-hardening` | A Lisa gate/skill/template did something wrong — a harness bug the kernel must fix. | none |
| Contribution | `template-candidate` | A generalizable pattern discovered downstream that would benefit every host project — not a defect, a proposed improvement to Lisa's templates/rules/skills. | `## Proposed template change` |

1. **Dedupe first.** `gh issue list -R <upstream> --state open --search "<fingerprint terms>"`
   — if an open issue already covers this failure class or pattern, comment the new
   occurrence on it (evidence compounds; duplicates dilute) and link it instead of filing.
2. **File with the same bar as any ticket:** a three-audience description (what failed or
   improved for the operator, what the harness did or could do, what to change), the
   verbatim evidence chain (PRD text → ticket AC → QA failure → gate that passed it; for
   patterns, the downstream occurrences proving generality), and the lane's label:
   `gh issue create -R <upstream> --title "<gate/skill>: <failure class>" --label self-hardening`
   for defects, or
   `gh issue create -R <upstream> --title "<template surface>: <pattern>" --label template-candidate`
   for contributions. A `template-candidate` filing MUST include a
   `## Proposed template change` section naming the Lisa template/rule/skill surface to
   change (e.g. `typescript/package-lisa/package.lisa.json`, a `plugins/src/base` rule)
   and the proposed content or diff sketch, so Lisa-side intake can triage it as a
   contribution rather than a defect.
3. **Close the loop.** Reference the upstream issue URL in the triage comment. The upstream
   repo's own build intake implements it; the next kernel release ships the hardening (or
   the generalized pattern) to every host project.

## Verdict

```text
## Verdict: [NOT_REWORK | REWORK_CLASSIFIED | ALREADY_TRIAGED]

**Signal:** [status-history | merged-pr | claim-marker | label | n/a]
**Primary cause:** [cause | n/a]
**Secondary cause:** [cause | none]
**Hardening filed:** [upstream <url> | tracker <key> | prd-comment | none | pending-human]
```

The caller (ticket-triage Phase 2.5, or a standalone invocation) incorporates this into its
own findings. `REWORK_CLASSIFIED` never blocks the fix — the rework ticket proceeds to
implementation regardless; hardening runs alongside, not in front of, shipping the fix. The
one exception: `missing-tool-access` with the gap still present should surface as a Phase 3
ambiguity in the calling triage ("prior attempt lacked <tool>; it is still unavailable"),
which blocks per the normal triage rules.

## Rules

- Never classify without evidence; `UNCLASSIFIED` beats a guess.
- One primary cause. Record a secondary only when the evidence genuinely supports both.
- Fire only on confirmed rework — first-attempt tickets exit at Phase 1 with `NOT_REWORK`.
- All ticket/issue writes go through `lisa-tracker-write` or `gh` as specified — never
  bypass the quality gates this loop exists to strengthen.
- Noise discipline: one triage comment per bounce (fingerprinted), no upstream issue without
  the dedupe search, and upstream issues describe failure *classes*, not single incidents.
- Never key on a durable mark whose inverse you have not checked. A signal with no expiry
  degrades toward meaning "this item has been around a while"
  (`state-changes-without-inverses`), and a deterministic input that degrades is
  deterministically wrong.

