# Lisa Verify Prd

> Initiative-level PRD acceptance gate. Given a PRD ref/URL (GitHub, Linear, Notion, Confluence, or JIRA), resolves the source vendor, reads the PRD and its generated top-level child work via the prd-lifecycle-rollup contract, and confirms every required child is terminal before any verification runs — if any is non-terminal it reports the incomplete set and STOPS. When the guard passes it runs spec-conformance against the original PRD requirements plus empirical verification via verification-lifecycle. On CONFORMS with all checks passing: transitions the PRD shipped → verified and posts evidence. On PARTIAL/DIVERGES or any failing check: re-opens the PRD shipped → ticketed (NEVER blocked), creates build-ready fix tickets for each divergence, and posts a product-readable failure report — the fix tickets auto-build, rollup re-ships the PRD, and a later intake cycle re-verifies, so the loop closes itself. Idempotent re-runs: comments regenerate in place via sentinel markers; fix tickets dedupe by stable ref.

- Skill: `codyswanngt/lisa-verify-prd` (Agent Skill, multi-file: 2 files)
- Install (CLI): `npx skillmds@latest add codyswanngt/lisa-verify-prd`
- Raw SKILL.md: https://api.skillmd.com/api/skills/codyswanngt/lisa-verify-prd/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-verify-prd

---


# PRD-level Verification: $ARGUMENTS

`/lisa:verify-prd <prd>` is the **initiative-level acceptance gate**. It runs *after* a PRD is `shipped` (all generated top-level work terminal, per `prd-lifecycle-rollup`'s rollup phase) and proves the shipped product actually matches the PRD — not merely that every ticket is closed. `shipped` is **necessary but not sufficient** for `verified`: a PRD can have every child ticket closed while still missing a requirement, diverging from its acceptance criteria, or failing a real user workflow.

This is distinct from `/lisa:verify`, which empirically verifies a **single work item** (a ticket / story / sub-task) in its target environment as part of that item's Build/Fix/Improve flow. `/lisa:verify` drives a build ticket to `done` at the leaf/build level; it does not read the PRD or judge initiative-level acceptance. `/lisa:verify-prd` operates one level up, over the whole initiative. See the `prd-lifecycle-rollup` rule's "PRD-level verification vs ticket verification" section for the full distinction.

`$ARGUMENTS` is a single PRD reference: a **GitHub issue URL** (or `<org>/<repo>#<n>`), a **Linear project/issue URL**, a **Notion page URL**, a **Confluence page URL**, or a **JIRA issue key/URL**.

## Confirmation policy

Do **not** re-prompt once invoked. Like the `*-prd-intake` skills, the caller has already authorized the run by invoking the skill; asking the user to confirm before reading or before applying the guard defeats the purpose of a batchable acceptance gate. Run the front-half (resolve → read child set → guard) to completion and report.

## Scope of this skill

This skill covers the **read/guard front-half**, **both verdict branches** (PASS and FAIL), and the **idempotency** of PRD-level verification:

1. Resolve the PRD ref and detect its source vendor.
2. Read the PRD body and its **generated top-level child work set** via the `prd-lifecycle-rollup` contract.
3. Apply the per-vendor terminal predicate to the generated top-level work and run the **terminal-child guard**: if any required top-level child is non-terminal, report the incomplete set and STOP — do not run verification, do not transition the PRD.
4. **Spec conformance** — when the guard passes, invoke the `spec-conformance` skill with the PRD as the spec source to build a section-by-section coverage matrix against the shipped product (never reimplementing the matrix).
5. **Empirical verification** — invoke `verification-lifecycle` to run empirical checks appropriate to the PRD's surface (browser/computer-use, API, CLI, DB, screenshots, logs), honoring the `verification` rule. Quality gates (test/typecheck/lint) are **not** verification.
6. **PASS transition + evidence** — when spec conformance is `CONFORMS` **and** every applicable empirical check passes, transition the PRD lifecycle from the resolved `shipped` role to the resolved `verified` role (vendor-neutral via `config-resolution`) and post verification evidence (the coverage matrix + empirical proof artifacts) back on the PRD.
7. **FAIL — re-open as `ticketed` + build-ready fix tickets (self-healing, never `blocked`)** — when spec conformance is `PARTIAL`/`DIVERGES`, or any applicable empirical check fails (or a required surface is unavailable), move the PRD from the resolved `shipped` role back to the resolved `ticketed` role, create **build-ready fix tickets** via `tracker-write` (`build_ready: true`) for each missing/incorrect/divergent behavior — **added to the PRD's generated top-level work** — and post a product-readable failure report (with the verification-round count). PRD verification **never** moves the PRD to `blocked`. The fix tickets auto-build, rollup (`*-prd-intake` Phase 3f) re-ships the PRD once they are terminal, and a later intake cycle (Phase 3g) re-verifies — the loop closes itself.
8. **Idempotency** — every write in Phases 6 and 7 is safe to re-run: evidence/failure-report comments carry a stable sentinel marker and are **regenerated in place** (never appended), fix tickets are deduped by a stable PRD-ref + requirement marker (**referenced/updated, never duplicated**), and the lifecycle transition is a **no-op when the PRD already carries the target role** (exactly one lifecycle label/status remains). See **Phase 8 — Idempotency** for the per-write guards.

When verification passes, this skill runs the **PASS** branch of Phase 6 (`shipped → verified`). When it does not pass — spec conformance not `CONFORMS`, or any empirical check failing — this skill runs the **FAIL** branch of Phase 7 (`shipped → ticketed` + build-ready fix tickets + failure report); it does **not** leave the PRD at `shipped` and **never** uses `blocked` (PRD verification is self-healing — the fix tickets auto-build and the PRD re-ships and re-verifies). Re-running the skill against the same PRD produces no duplicate evidence comments, no duplicate fix tickets, and no duplicate lifecycle labels/statuses — the **Phase 8** guards make each Phase 6/7 write idempotent, exactly as `prd-backlink` regenerates its `## Tickets` section in place and `github-prd-intake` no-ops a rollup on an already-shipped PRD.

## Phase 1 — Resolve the PRD ref and detect the source vendor

Detect the vendor from `$ARGUMENTS` the same way `prd-ticket-coverage` / `prd-backlink` do — from the host (or key shape for JIRA), never by guessing:

| Input shape | Vendor | Read surface |
|---|---|---|
| `github.com/<org>/<repo>/issues/<n>` or `<org>/<repo>#<n>` | **GitHub Issues** | `gh` CLI (Lisa uses the CLI exclusively for GitHub — no GitHub MCP) |
| `linear.app/...` | **Linear** | `lisa-linear-access operation: get-project` / `lisa-linear-access operation: get-issue` |
| `notion.so` / `notion.site` | **Notion** | `lisa-notion-access` — fetch the page **with its discussions** |
| `*.atlassian.net/wiki/...` | **Confluence** | `lisa-atlassian-access` — read the page and its descendants |
| JIRA issue key (e.g. `PROJ-123`) or `*.atlassian.net/browse/...` | **JIRA** | `lisa-atlassian-access` — read the issue (and JQL-search when resolving a set) |

State the operation you need, never a vendor tool name: the access skills own substrate selection, and a hardcoded MCP tool name is not portable. The same server registers under different prefixes depending on install path (`mcp__atlassian__*`, `mcp__claude_ai_Atlassian__*`, `mcp__plugin_atlassian_atlassian__*`), and its data tools register only once OAuth completes — so a named tool fails outright in a context that has the CLI or token substrate available and would otherwise have worked. See the `integration-access-layer` rule.

Read the PRD body via the vendor-appropriate surface. The vendor that owns the PRD source is what Phase 2 reads the child set from; it is independent of which tracker hosts the generated tickets (a Notion PRD can own JIRA tickets — the cross-vendor case is handled by the documented generated-work section in Phase 2).

Where the PRD lives in the same vendor as the configured `tracker` (`.lisa.config.json`), prefer reading the PRD ticket through `lisa-tracker-read` so this skill stays agnostic of which tracker hosts the work — `tracker-read` dispatches to `lisa-github-read-issue` / `lisa-jira-read-ticket` / `lisa-linear-read-issue` per config and returns the consolidated context bundle (PRD body, native children, links). For PRD sources with no tracker counterpart (Notion / Confluence / cross-vendor), read the PRD body directly via the vendor surface above.

## Phase 2 — Read the generated top-level child work set

**Do NOT reimplement child enumeration.** Consume the PRD→generated-top-level-work relationship recorded by the merged child-linking work (`prd-backlink` native linking + its always-written machine-readable generated-work section), exactly as `github-prd-intake`'s rollup phase consumes it. Read the **generated top-level work** only — the PRD's created Epics and any top-level Story created directly under it — and **exclude** leaf Sub-tasks and any Story nested under a generated Epic, per the `prd-lifecycle-rollup` rule's generated-top-level-work contract.

Use two sources, **native first**, deduped by child-ref identity:

1. **Native hierarchy (primary).** Read the PRD's direct native children — these are its top-level children:
   - **GitHub** — the PRD issue's direct `subIssues` nodes (the GraphQL `subIssues` query `lisa-github-read-issue` Phase 3 uses; capture `number title state url stateReason labels`).
   - **Linear** — for a PRD Project, its member Issues (`list_issues({project})`); for a PRD Issue, its sub-Issues (`get_issue` children). Capture each child's `state` (workflow state + category).
   - **JIRA** — the PRD's children via the epic-link/parent JQL `lisa-jira-read-ticket` Phase 5 uses (`"Epic Link" = <PRD-KEY>` or `parent = <PRD-KEY>`). Capture each child's `statusCategory`.
   - **Notion / Confluence** — no native issue hierarchy; use the documented section below.

2. **Documented generated-work section (fallback / cross-vendor).** When native hierarchy is unavailable (older host, cross-vendor PRD→tracker, or the relationship was only recorded in the PRD body), parse the machine-readable generated-work section `prd-backlink` writes to the PRD body (`## Tickets`, alias `## Generated Work`). Enumerate the `<!-- lisa:gw ref=… url=… type=… parent=… -->` tokens; the **generated top-level child set is every token whose `parent` is empty** (top-level), exactly as `prd-backlink` documents. Tokens with a non-empty `parent` are descendants — skip them. For GitHub, this is the same `awk` extraction `github-prd-intake` Phase 3f.2 uses.

**Dedupe by child-ref identity** (`owner/repo#number` for GitHub, the issue/project identifier for Linear, the issue key for JIRA, the recorded ref for Notion/Confluence — the `prd-lifecycle-rollup` idempotency dedupe key) so a child appearing in both the native graph and the documented section is counted once. **Match by stable ref, never by title.**

If neither source yields any generated top-level child (the PRD generated nothing, or the relationship was never recorded), report `no generated top-level children — cannot verify an empty PRD` and STOP. Do not transition the PRD.

## Phase 3 — Terminal-child guard

Apply the **per-vendor terminal-state predicate from the `prd-lifecycle-rollup` rule** to every generated **top-level** work item (cite the rule by slug — do not restate its predicate table here). In summary, a top-level child is:

- **Terminal** — it has reached its source/tracker's done/shipped state (GitHub: closed + the resolved build `done` role label where used; Linear: a `done`-category completed state; JIRA: `statusCategory.key == "done"`; Notion/Confluence: the documented generated-work entry marked done). A generated Epic is terminal only when *it* has rolled up to its own terminal state per `leaf-only-lifecycle` — read the child's own resolved state; do not re-derive it from its leaves.
- **Terminal-but-dropped** — closed-as-not-planned (GitHub `stateReason == "not_planned"`) / canceled or duplicate (Linear — both are terminal `state.type`s; `duplicate` is distinct from `canceled`) / won't-do. It does **not** hold the PRD open and is excluded from the required set.
- **Incomplete / blocked** — anything else (still open, or closed without the `done` role). It holds the PRD open.

The **required** set is the top-level children minus the terminal-but-dropped ones. Branch:

**Any required top-level child is non-terminal** (the "Given a PRD has generated child work that is not terminal" scenario):

1. **STOP.** Do **not** run empirical verification or spec-conformance.
2. **Leave the PRD lifecycle untouched** — it stays at `shipped`. Do not transition it to `verified` or `blocked`; do not close or archive it.
3. **Report the incomplete child set** — list each non-terminal required top-level child as `- <ref> "<title>" — <state>`, so product can see exactly what is blocking PRD-level verification.

This guard exists because PRD-level acceptance is only meaningful once the work graph is actually complete. `shipped` is the precondition; verifying a PRD whose generated work is still in flight would produce a false PASS or FAIL against an incomplete product.

**All required top-level children are terminal** (at least one required child exists): the guard passes. Proceed to **Phase 4 — Spec conformance** and the rest of the PASS path below. The guard is the precondition for verification; only once the work graph is complete is it meaningful to check the shipped product against the PRD.

## Phase 4 — Spec conformance against the PRD

The guard proves the work graph is *complete*; spec conformance proves the shipped product matches *what the PRD asked for*, section by section. This is the "accountant lens" — did the initiative ship exactly the PRD's requirements, nothing silently dropped, nothing scope-crept?

**Do NOT reimplement the coverage matrix.** Invoke the existing `spec-conformance` skill with the PRD as the spec source:

```text
/spec-conformance <PRD ref/URL>
```

Pass the same `$ARGUMENTS` PRD reference resolved in Phase 1. `spec-conformance` Phase 1 already accepts a GitHub issue URL / Linear identifier / JIRA key / Notion / Confluence PRD as its spec source and loads the full PRD body (via `tracker-read` or the vendor surface), so the PRD is the spec here — not a plan file or a leaf ticket. It then:

- extracts every PRD requirement into a structured list (acceptance criteria, Out of Scope, technical commitments, Validation Journey assertions, deliverables);
- inspects the shipped product (the merged work across the generated top-level children) for evidence of each;
- builds a **section-by-section coverage matrix** mapping each requirement to evidence;
- flags scope creep (Out-of-Scope violations) and untraceable changes separately from misses;
- returns a verdict: **`CONFORMS`**, **`PARTIAL`**, or **`DIVERGES`**.

Capture the coverage matrix and verdict verbatim — both feed the evidence comment in Phase 6 and gate the PASS branch.

Spec conformance at the PRD level differs from the leaf-ticket conformance run during a single item's Build/Fix/Improve flow: the spec is the **PRD itself** (the whole initiative's requirements), and the shipped work is the **union** of all generated top-level children, not one branch's diff. The `spec-conformance` skill handles both because its spec source is parameterized; this skill simply hands it the PRD.

**Branch on the verdict:**

- **`CONFORMS`** — continue to Phase 5 (empirical verification).
- **`PARTIAL` or `DIVERGES`** — verification has **not** passed. Do **not** run Phase 5 (empirical verification adds nothing once the spec already diverges) and do **not** enter the PASS path. Record the verdict and the matrix, then go to **Phase 7 — FAIL** with a `CONFORMANCE_FAILED` cause: the failed/missing/scope-crept requirements the matrix flagged become the failure report's findings and the seeds for the linked fix issues.

## Phase 5 — Empirical verification of the shipped surface

Spec conformance reads the diff and the requirements; empirical verification **runs the actual shipped system and observes results**. Quality gates (tests, typecheck, lint) are prerequisites, **not** verification — a green test suite never substitutes for exercising the shipped product (see the `verification` rule).

**The verification surface is PRD-dependent.** A PRD that shipped a UI flow is verified through the browser; one that shipped an API through request/response captures; a CLI through command runs; a schema change through database queries; a background job through logs and queue inspection. Do not assume a fixed surface — classify it from what the PRD actually delivered.

**Do NOT reinvent the verification machinery.** Invoke the `verification-lifecycle` skill and follow its mandatory sequence (confirm quality gates → classify empirical types → check tooling → fail-fast → plan → execute → codify → loop) against the *shipped initiative*:

```text
/verification-lifecycle
```

1. **Classify** the empirical verification types that apply to the PRD's shipped surface, per the Verification Types table in the `verification` rule.
2. **Discover tooling** for each type (browser/computer-use MCP, HTTP client, DB client, CLI, log/metrics access) via the lifecycle's Tool Discovery Process. For a UI surface, reuse the `product-walkthrough` skill to drive the live product through a real browser and ground verification (and the eventual evidence comment) in what actually renders.
3. **Plan** each check: the exact tool/command, the expected pass outcome, and any prerequisites (running service, seeded data, auth). A plan that lists only `test`/`typecheck`/`lint` is not a verification plan.
4. **Execute** each check and collect **proof artifacts** (screenshots, request/response captures, query outputs, log excerpts with correlation IDs) per the lifecycle's Proof Artifacts Requirements.
5. **Codify** — for each passing empirical verification, the lifecycle invokes `codify-verification` to encode it as a regression test (Playwright for UI, integration test for API/DB, benchmark for performance) so the PRD's behavior cannot silently regress. Honor that step; it is mandatory for every empirical type except the inherently non-behavioral set the lifecycle exempts.

If a required surface or its tooling is unavailable, follow the lifecycle's Escalation Protocol — declare the verification level (PARTIALLY VERIFIED / UNVERIFIED) and surface the gap rather than declaring a false PASS.

**Branch on the result:**

- **Every applicable empirical check passes** (and is codified where the lifecycle requires) — continue to Phase 6 (PASS).
- **Any applicable empirical check fails, or a required surface is unavailable** — verification has **not** passed. Record what failed/was-blocked (the check, the tool/command, observed vs expected, and any proof artifacts captured), then go to **Phase 7 — FAIL** with an `EMPIRICAL_FAILED` cause: each failed check (or unavailable required surface) becomes a failure-report finding and a seed for a linked fix issue.

> **Single-environment note.** In a single-environment project (`main`/production only, no dev/staging), the shipped surface is whatever production exposes. A project with no deployed application, sign-in, or end-to-end environment variables (Lisa itself) verifies on its CLI/dry-run surface — running the documentation/skill build and drift check — which is the empirical surface the PRD's Validation Journey declares. The surface is always PRD-dependent: read the PRD's Empirical Verification Plan and verify what it says ships.

## Phase 6 — PASS: transition `shipped → verified` and post evidence

Reach this phase **only** when **both** are true:

- Phase 4 spec conformance returned **`CONFORMS`**, and
- Phase 5 every applicable empirical check **passed** (and was codified where required).

If either is false, do not enter this phase — record the verdict and route to **Phase 7 — FAIL**, which owns the `shipped → ticketed` (re-open + build-ready fix tickets) path.

### 6.1 — Resolve the `verified` and `shipped` roles

Resolve the PRD-lifecycle roles from `.lisa.config.json` (then `.lisa.config.local.json` override) per the `config-resolution` rule — the same role-resolution the `*-prd-intake` skills use. **Cite `config-resolution` for the role vocabulary; do not hardcode label strings except as the documented defaults.** Resolution per vendor:

| Vendor | `shipped` role | `verified` role | Default `verified` |
|---|---|---|---|
| **GitHub** | `github.labels.prd.shipped` | `github.labels.prd.verified` | `prd-verified` (label) |
| **Linear** | `linear.labels.prd.shipped` | `linear.labels.prd.verified` | `prd-verified` (project/issue label) |
| **Notion** | `notion.values.shipped` | `notion.values.verified` | `Verified` (status value) |
| **Confluence** | `confluence.parents.shipped` | `confluence.parents.verified` | the `Verified` parent page id |
| **JIRA** | the configured shipped status | the configured verified status | per `config-resolution` |

For GitHub, resolve with the same helper `github-prd-intake` uses:

```bash
read_role() {  # role default → resolved value (local override wins)
  local role="$1" default="$2" local_v global_v
  local_v=$(jq -r ".github.labels.prd.${role} // empty" .lisa.config.local.json 2>/dev/null)
  global_v=$(jq -r ".github.labels.prd.${role} // empty" .lisa.config.json 2>/dev/null)
  echo "${local_v:-${global_v:-$default}}"
}
SHIPPED=$(read_role shipped  prd-shipped)
VERIFIED=$(read_role verified prd-verified)
```

### 6.2 — Transition the PRD `shipped → verified`

**Idempotency guard (no-op if already verified).** Before transitioning, read the PRD's current lifecycle role. If the PRD **already carries `$VERIFIED`**, the transition is a **no-op** — do not re-add the label/status, do not re-remove `$SHIPPED` (already gone). Record it as `already verified (no-op)` and proceed to 6.3 (the evidence comment is still refreshed in place, per Phase 8). This mirrors `github-prd-intake` Phase 3f.1's "no-op if already shipped" guard and the `prd-lifecycle-rollup` "rollup is keyed by the PRD's current state" rule (cite both by slug).

Otherwise apply the vendor-appropriate transition. This is the `shipped → verified` PASS hop the `prd-lifecycle-rollup` rule defines (cite it by slug; this skill is its PASS-path implementation, not a second source of truth):

- **GitHub / Linear** — remove the `shipped` label and add the `verified` label. For GitHub:
  ```bash
  gh issue edit <prd-num> --repo <org>/<repo> --remove-label "$SHIPPED" --add-label "$VERIFIED"
  ```
  Verify exactly **one** PRD-lifecycle label remains afterward (the single-label invariant `github-prd-intake` enforces) — a re-run must never leave both `$SHIPPED` and `$VERIFIED`, nor two copies of `$VERIFIED`. For GitHub, close the PRD issue immediately after the label transition with `gh issue close <prd-num> --repo <org>/<repo> --reason completed`; if it is already closed, record that native closure was already satisfied. For Linear, set the project/issue label equivalently, then archive/complete the project or issue using the vendor's native terminal mechanism where available.
- **Notion** — set the PRD page's `notion.statusProperty` (default `Status`) to the resolved `verified` value (default `Verified`). A status property holds exactly one value, so re-setting the same value is inherently a no-op. Then archive the page through `lisa-notion-access` where supported; if the page is already archived, record that native archival was already satisfied.
- **Confluence** — move the PRD page's `parentId` to `confluence.parents.verified` (the parent-page-based lifecycle; Atlassian scoped tokens cannot write labels — see `config-resolution`). A page has exactly one parent, so re-parenting to the same parent is a no-op. Then archive the page where the deployment supports archival; if archival is unavailable, report the capability-aware no-op or setup gap.
- **JIRA** — transition the PRD issue to the configured `verified` status. An issue holds exactly one status; if already `verified`, skip the transition. Then resolve/close the issue using the configured terminal workflow transition where supported.

`verified` is the terminal, product-owned PRD state; this skill is the **only** automated writer of it (intake/rollup never set it). After the verified transition, close, archive, or complete the PRD natively where the source vendor supports it. This close-out is mandatory and idempotent; do not introduce a configuration flag to skip it.

### 6.3 — Post verification evidence on the PRD

Post a verification-evidence comment back on the PRD, in the spirit of `tracker-evidence` (the vendor-neutral evidence poster). Because the evidence lands on the **PRD source** — which may be Notion or Confluence, not a tracker ticket — post via the vendor surface that owns the PRD: `gh issue comment` for GitHub, the Linear comment API, the Notion/Confluence page comment surface, or a JIRA comment. Where the PRD lives in the configured `tracker`, you may dispatch through `tracker-evidence`; for Notion/Confluence/cross-vendor PRDs, comment on the PRD page directly.

**Idempotent — regenerate the evidence comment in place, never append.** Lead the comment body with the stable sentinel marker `<!-- lisa:verify-prd-evidence -->`. Before posting, look up an existing evidence comment authored by this skill on the PRD whose body contains that sentinel (the same regenerate-don't-append discipline `prd-backlink` uses for its `## Tickets` section; **match by the marker, never by comment text or position**). If one exists, **edit it in place** with the freshly regenerated body; only create a new comment when none exists. Per vendor:

- **GitHub** — `gh issue view <prd-num> --repo <org>/<repo> --json comments` and select the comment whose `body` contains `<!-- lisa:verify-prd-evidence -->`; update it with `gh api -X PATCH /repos/<org>/<repo>/issues/comments/<comment-id> -f body=@evidence.md`. Only `gh issue comment <prd-num> --body-file evidence.md` when no marked comment exists.
- **Linear** — find the existing comment carrying the sentinel and update it via the Linear comment-update API; create only if absent.
- **Notion / Confluence** — update the existing marked page comment in place; create only if absent.
- **JIRA** — update the existing marked comment in place; create only if absent.

The marked comment is the single canonical evidence comment for the PRD — a re-run refreshes it, never stacking a second one. The evidence comment MUST include:

1. **Sentinel marker** — the literal `<!-- lisa:verify-prd-evidence -->` as the first line, so the next run finds and regenerates this exact comment.
2. **AI disclosure** — lead with "PRD-level verification by Claude (AI agent, not a human)."
3. **Verdict line** — `shipped → verified — PASS`.
4. **Spec-conformance coverage matrix** — the section-by-section matrix from Phase 4 verbatim, with the `CONFORMS` verdict.
5. **Empirical proof artifacts** — the Phase 5 artifacts per surface: screenshots (upload via `gh release upload pr-assets <files> --clobber` and reference as plain URLs, per the `tracker-evidence` UI Evidence Checklist), request/response captures, query outputs, log excerpts, and the codified regression test(s) added.
6. **What was verified** — which PRD acceptance criteria each artifact covers, and the verification surface used.

Then emit the PASS output block (below).

## Phase 7 — FAIL: re-open as `ticketed`, create build-ready fix tickets, post a failure report (never `blocked`)

Reach this phase when verification did **not** pass — i.e. **either** is true:

- Phase 4 spec conformance returned **`PARTIAL`** or **`DIVERGES`** (`CONFORMANCE_FAILED` cause), or
- Phase 5 had **any** applicable empirical check fail or a required surface unavailable (`EMPIRICAL_FAILED` cause).

PRD verification failure is **self-healing, not a dead end**. Instead of parking the PRD in `blocked` for a human, this phase: (1) moves the PRD back to `ticketed` (work in flight again), (2) creates **build-ready fix tickets** for the gaps so the build queue picks them up with no human promotion, and (3) posts a failure report. The fix tickets are added to the PRD's generated top-level work, so the existing machinery closes the loop on its own: the fix tickets build → reach terminal → the `*-prd-intake` rollup (Phase 3f) re-ships the PRD `ticketed → shipped` → the next intake cycle (Phase 3g) re-dispatches `/lisa:verify-prd` → PASS gives `verified`, FAIL runs this phase again with another round of fix tickets. This is the FAIL counterpart of the Phase 6 PASS hop and is the `shipped → ticketed` FAIL hop the `prd-lifecycle-rollup` rule's "Closing the loop" section defines (cite it by slug; this skill is its FAIL-path implementation, not a second source of truth).

**PRD verification never moves the PRD to `blocked`.** `blocked` is the *intake* (ready-stage validation) failure state, not the verification failure state — there is no `prd-verifying` / `prd-verification-failed` state either; the lifecycle stays small. The loop never auto-halts; the failure report carries a **verification-round count** so a human can spot a PRD stuck across repeated rounds, but the skill keeps creating fix tickets and re-verifying.

Carry forward the verdict cause and the concrete **findings** that produced it: from `CONFORMANCE_FAILED`, the matrix's missed/divergent/scope-crept requirements; from `EMPIRICAL_FAILED`, each failing check (the requirement/AC it exercised, the tool/command, observed vs expected, and any captured artifacts). These findings drive both the failure report (7.3) and the fix issues (7.4).

### 7.1 — Resolve the `shipped` and `ticketed` roles

Resolve the PRD-lifecycle roles from `.lisa.config.json` (then `.lisa.config.local.json` override) per the `config-resolution` rule — the same role-resolution Phase 6.1 and the `*-prd-intake` skills use. **Cite `config-resolution` for the role vocabulary; do not hardcode label strings except as the documented defaults.** Resolution per vendor:

| Vendor | `shipped` role | `ticketed` role | Default `ticketed` |
|---|---|---|---|
| **GitHub** | `github.labels.prd.shipped` | `github.labels.prd.ticketed` | `prd-ticketed` (label) |
| **Linear** | `linear.labels.prd.shipped` | `linear.labels.prd.ticketed` | `prd-ticketed` (project/issue label) |
| **Notion** | `notion.values.shipped` | `notion.values.ticketed` | `Ticketed` (status value) |
| **Confluence** | `confluence.parents.shipped` | `confluence.parents.ticketed` | the `Ticketed` parent page id |
| **JIRA** | the configured shipped status | the configured ticketed status | per `config-resolution` |

For GitHub, resolve with the same helper Phase 6.1 / `github-prd-intake` use:

```bash
read_role() {  # role default → resolved value (local override wins)
  local role="$1" default="$2" local_v global_v
  local_v=$(jq -r ".github.labels.prd.${role} // empty" .lisa.config.local.json 2>/dev/null)
  global_v=$(jq -r ".github.labels.prd.${role} // empty" .lisa.config.json 2>/dev/null)
  echo "${local_v:-${global_v:-$default}}"
}
SHIPPED=$(read_role shipped prd-shipped)
TICKETED=$(read_role ticketed prd-ticketed)
```

### 7.2 — Re-open the PRD `shipped → ticketed`

**Idempotency guard (no-op if already ticketed).** Before transitioning, read the PRD's current lifecycle role. If the PRD **already carries `$TICKETED`** (the common re-run case — a prior failed round already re-opened it and its fix tickets are still in flight), the transition is a **no-op** — do not re-add the label/status, do not re-remove `$SHIPPED` (already gone). Record it as `already ticketed (no-op)` and proceed to 7.3, where the existing failure report is **updated in place** (not stacked, round count incremented) and 7.4, where existing fix tickets are **referenced** rather than re-created. This mirrors `github-prd-intake` Phase 3f.1's "no-op if already shipped" guard and the `prd-lifecycle-rollup` "rollup is keyed by the PRD's current state" rule (cite both by slug).

Otherwise apply the vendor-appropriate transition. This is the `shipped → ticketed` FAIL hop from the `prd-lifecycle-rollup` rule's "Closing the loop" section (cite it by slug) — a deliberate backward hop that puts the PRD back into "work in flight" so its new fix tickets are the in-flight work the rollup waits on:

- **GitHub / Linear** — remove the `shipped` label and add the `ticketed` label. For GitHub:
  ```bash
  gh issue edit <prd-num> --repo <org>/<repo> --remove-label "$SHIPPED" --add-label "$TICKETED"
  ```
  Verify exactly **one** PRD-lifecycle label remains afterward (the single-label invariant `github-prd-intake` enforces) — a re-run must never leave both `$SHIPPED` and `$TICKETED`, nor two copies of `$TICKETED`. For Linear, set the project/issue label equivalently.
- **Notion** — set the PRD page's `notion.statusProperty` (default `Status`) to the resolved `ticketed` value (default `Ticketed`). A status property holds exactly one value, so re-setting the same value is inherently a no-op.
- **Confluence** — move the PRD page's `parentId` to `confluence.parents.ticketed` (the parent-page-based lifecycle; Atlassian scoped tokens cannot write labels — see `config-resolution`). A page has exactly one parent, so re-parenting to the same parent is a no-op.
- **JIRA** — transition the PRD issue to the configured `ticketed` status. An issue holds exactly one status; if already `ticketed`, skip the transition.

Do **not** close or archive the PRD here, and **never** move it to `blocked` — `ticketed` signals "verification found gaps; fix work is in flight," and the PRD re-ships and re-verifies automatically once that work lands.

### 7.3 — Post a product-readable failure report on the PRD

Post a **failure report** comment back on the PRD, via the same vendor surface Phase 6.3 uses for evidence (`gh issue comment` for GitHub, the Linear comment API, the Notion/Confluence page comment surface, or a JIRA comment; dispatch through `tracker-evidence` where the PRD lives in the configured `tracker`). The report is written for a **non-engineer product owner** — plain language, no stack traces dumped raw. Capture its URL/anchor so the fix issues in 7.4 can back-link to it.

**Idempotent — regenerate the failure report in place, never append.** Lead the comment body with the stable sentinel marker `<!-- lisa:verify-prd-failure-report -->`. Before posting, look up an existing failure-report comment on the PRD whose body contains that sentinel (**match by the marker, never by comment text or position** — the same regenerate-don't-append discipline as Phase 6.3). If one exists, **edit it in place** with the freshly regenerated findings (so a re-run-after-a-previous-failure refreshes the same report rather than stacking a second one); only create a new comment when none exists. The GitHub mechanics are identical to Phase 6.3 (`gh issue view --json comments` to find the marked comment, `gh api -X PATCH .../issues/comments/<id>` to update, `gh issue comment` only when absent). It MUST include:

1. **Sentinel marker** — the literal `<!-- lisa:verify-prd-failure-report -->` as the first line, so the next run finds and regenerates this exact comment.
2. **AI disclosure** — lead with "PRD-level verification by Claude (AI agent, not a human)."
3. **Verdict line + round** — `shipped → ticketed — FAIL (re-opened for fixes)`, the cause (`CONFORMANCE_FAILED` or `EMPIRICAL_FAILED`), and `Round: N` — the count of failed verification rounds for this PRD. Read the prior in-place failure report's round and increment (start at 1). The loop **never auto-halts** on a high count, but surfacing it lets a human notice a PRD stuck across repeated fix-and-re-verify rounds.
4. **What failed, in plain language** — for each finding, name the **specific PRD requirement / acceptance criterion** that was not met, then **what was expected vs what was observed** (the empirical evidence: what was checked, what the shipped product did instead). One bullet per finding so product can follow each independently.
5. **Spec-conformance coverage matrix** — for a `CONFORMANCE_FAILED` cause, the section-by-section matrix from Phase 4 verbatim with the `PARTIAL`/`DIVERGES` verdict, so the missed/divergent/scope-crept rows are visible.
6. **Proof artifacts** — any captured empirical artifacts (screenshots uploaded via `gh release upload pr-assets <files> --clobber` and referenced as plain URLs per the `tracker-evidence` UI Evidence Checklist, request/response captures, query outputs, log excerpts).
7. **Fix issues** — a list of the fix issues created/referenced in 7.4 (filled in after 7.4 runs, or posted as a brief follow-up edit), so the report is the single product-facing index of "what's wrong and where it's being fixed."

### 7.4 — Create linked fix issues for the missing/incorrect behavior

For **each** failed/missing/incorrect/divergent finding, create a **build-ready fix ticket** via `tracker-write` with **`build_ready: true`** (the vendor-neutral writer) — never by hand-rolling `gh issue create`, so each ticket passes the same quality gates (`tracker-validate`) every Lisa ticket does: three-audience description, **Gherkin acceptance criteria**, labels, and explicit relationship discovery. `build_ready: true` makes the build queue (`lisa-intake` build side / `*-build-intake`) auto-claim it with **no human promotion** — that is what makes the loop self-healing. Group findings that share one root cause into one fix ticket; do not fan out one ticket per matrix cell when several rows are the same defect.

**Idempotent — dedupe fix issues by a stable marker; reference/update, never duplicate.** This is the "re-run after a previous failure with the same missing behavior" scenario: the prior run already opened a fix issue for that requirement, so the re-run must **find and reuse it**, not create a second one. Apply the `prd-lifecycle-rollup` idempotency dedupe key discipline (**match by a stable ref, never by title**):

1. **Compute a stable dedupe key per finding** — the PRD ref plus a stable requirement/AC identity (e.g. the AC's heading/number or a slug of the requirement), independent of any mutable wording. Encode it in the fix-issue body as the marker `<!-- lisa:verify-prd-fix prd=<prd-ref> req=<stable-req-id> -->`.
2. **Look up an existing OPEN fix issue carrying that exact marker** before creating anything. On GitHub, search the repo for open issues whose body contains the marker (`gh issue list --repo <org>/<repo> --state open --search '"<!-- lisa:verify-prd-fix prd=<prd-ref> req=<stable-req-id> -->"' --json number,url,body` — or fetch and grep the marker); on Linear/JIRA, query by the marker stored in the body/a custom field. **Match on the marker, never on the issue title** (a title may have been edited; the marker is the stable identity).
3. **If a matching open fix issue exists, reference/update it — do not create a duplicate.** Refresh its captured evidence (the latest observed-vs-expected) and re-affirm the back-links to the PRD and the regenerated failure report, then fold its existing ref into the failure report's **Fix issues** list. A closed prior fix issue does **not** suppress a new one — if the requirement is failing again after the fix was closed, that is a regression and a fresh fix issue is correct.
4. **Only when no matching open fix issue exists, create a new one** via `tracker-write`.

Each fix issue (whether freshly created or referenced/updated) MUST:

1. **Carry the dedupe marker** — `<!-- lisa:verify-prd-fix prd=<prd-ref> req=<stable-req-id> -->` in its body, so the next run finds and reuses it.
2. **Reference the specific failed requirement/AC** — quote or cite the exact PRD requirement / acceptance criterion the finding violated, so the fix is scoped to a real gap (not a vague "make it work").
3. **Carry the captured evidence** — the observed-vs-expected from the failure report (what was checked, what was expected, what the shipped product did), so an implementer can reproduce without re-deriving it.
4. **Back-link to the PRD and the failure report** — link to the PRD (so the fix rolls back up to the initiative) and to the failure-report comment from 7.3 (so the full context is one click away). On GitHub, reference the PRD issue number and the failure-report comment URL in the body and, where supported, as a sub-issue/`Relates to` link; on Linear, set the relation; on JIRA, add the issue link and remote link.
5. **Have acceptance criteria** — Gherkin ACs describing the corrected behavior (what "fixed" looks like), enforced by `tracker-write` → the vendor `*-validate-issue` gate.
6. **Be build-ready and counted as PRD work** — created via `tracker-write` with `build_ready: true`, and **registered in the PRD's generated top-level work**: refresh the PRD's `## Tickets` /

…(truncated)
