@codyswann/lisa 2.46.0 → 2.47.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (39) hide show
  1. package/package.json +1 -1
  2. package/plugins/lisa/.claude-plugin/plugin.json +1 -1
  3. package/plugins/lisa/.codex-plugin/plugin.json +1 -1
  4. package/plugins/lisa/agents/confluence-prd-intake.md +3 -3
  5. package/plugins/lisa/agents/github-prd-intake.md +3 -3
  6. package/plugins/lisa/agents/linear-prd-intake.md +3 -3
  7. package/plugins/lisa/agents/notion-prd-intake.md +3 -3
  8. package/plugins/lisa/rules/prd-lifecycle-rollup.md +23 -1
  9. package/plugins/lisa/skills/confluence-prd-intake/SKILL.md +7 -3
  10. package/plugins/lisa/skills/github-prd-intake/SKILL.md +6 -4
  11. package/plugins/lisa/skills/linear-prd-intake/SKILL.md +6 -4
  12. package/plugins/lisa/skills/notion-prd-intake/SKILL.md +7 -3
  13. package/plugins/lisa/skills/prd-backlink/SKILL.md +1 -0
  14. package/plugins/lisa-cdk/.claude-plugin/plugin.json +1 -1
  15. package/plugins/lisa-cdk/.codex-plugin/plugin.json +1 -1
  16. package/plugins/lisa-expo/.claude-plugin/plugin.json +1 -1
  17. package/plugins/lisa-expo/.codex-plugin/plugin.json +1 -1
  18. package/plugins/lisa-harper-fabric/.claude-plugin/plugin.json +1 -1
  19. package/plugins/lisa-harper-fabric/.codex-plugin/plugin.json +1 -1
  20. package/plugins/lisa-nestjs/.claude-plugin/plugin.json +1 -1
  21. package/plugins/lisa-nestjs/.codex-plugin/plugin.json +1 -1
  22. package/plugins/lisa-openclaw/.claude-plugin/plugin.json +1 -1
  23. package/plugins/lisa-openclaw/.codex-plugin/plugin.json +1 -1
  24. package/plugins/lisa-rails/.claude-plugin/plugin.json +1 -1
  25. package/plugins/lisa-rails/.codex-plugin/plugin.json +1 -1
  26. package/plugins/lisa-typescript/.claude-plugin/plugin.json +1 -1
  27. package/plugins/lisa-typescript/.codex-plugin/plugin.json +1 -1
  28. package/plugins/lisa-wiki/.claude-plugin/plugin.json +1 -1
  29. package/plugins/lisa-wiki/.codex-plugin/plugin.json +1 -1
  30. package/plugins/src/base/agents/confluence-prd-intake.md +3 -3
  31. package/plugins/src/base/agents/github-prd-intake.md +3 -3
  32. package/plugins/src/base/agents/linear-prd-intake.md +3 -3
  33. package/plugins/src/base/agents/notion-prd-intake.md +3 -3
  34. package/plugins/src/base/rules/prd-lifecycle-rollup.md +23 -1
  35. package/plugins/src/base/skills/confluence-prd-intake/SKILL.md +7 -3
  36. package/plugins/src/base/skills/github-prd-intake/SKILL.md +6 -4
  37. package/plugins/src/base/skills/linear-prd-intake/SKILL.md +6 -4
  38. package/plugins/src/base/skills/notion-prd-intake/SKILL.md +7 -3
  39. package/plugins/src/base/skills/prd-backlink/SKILL.md +1 -0
package/package.json CHANGED
@@ -82,7 +82,7 @@
82
82
  "lodash": ">=4.18.1"
83
83
  },
84
84
  "name": "@codyswann/lisa",
85
- "version": "2.46.0",
85
+ "version": "2.47.1",
86
86
  "description": "Claude Code governance framework that applies guardrails, guidance, and automated enforcement to projects",
87
87
  "main": "dist/index.js",
88
88
  "exports": {
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa",
3
- "version": "2.46.0",
3
+ "version": "2.47.1",
4
4
  "description": "Universal governance — agents, skills, commands, hooks, and rules for all projects",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa",
3
- "version": "2.46.0",
3
+ "version": "2.47.1",
4
4
  "description": "Universal governance: agents, skills, commands, hooks, and rules for all projects.",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -17,7 +17,7 @@ You are a PRD intake agent. Your single job is to run one intake cycle against t
17
17
 
18
18
  This agent is the Confluence counterpart of `notion-prd-intake`. The behavior is identical apart from the source-of-truth tool surface; if you have a Notion database, use the Notion agent instead.
19
19
 
20
- Label role names (`ready`, `in_review`, `blocked`, `ticketed`, `shipped`) are resolved from `.lisa.config.json` `confluence.labels.*` by the `confluence-prd-intake` skill. The defaults match the legacy hardcoded names (`prd-ready`, `prd-in-review`, `prd-blocked`, `prd-ticketed`, `prd-shipped`).
20
+ Label role names (`ready`, `in_review`, `blocked`, `ticketed`, `shipped`, `verified`) are resolved from `.lisa.config.json` `confluence.labels.*` (parent-page placement) by the `confluence-prd-intake` skill. The defaults match the legacy hardcoded names (`prd-ready`, `prd-in-review`, `prd-blocked`, `prd-ticketed`, `prd-shipped`, `prd-verified`). The full PRD lifecycle is `draft → ready → in_review → blocked | ticketed → shipped → verified`; this agent only ever drives `ready → in_review → blocked | ticketed`. The `shipped` rollup is owned by the intake skill's rollup phase, and `verified` is set by `/lisa:verify-prd` after empirical PRD-level acceptance — never by this agent.
21
21
 
22
22
  ## Confirmation policy
23
23
 
@@ -53,12 +53,12 @@ After a successful cycle, if any PRDs ended in the `blocked` label, mention to t
53
53
 
54
54
  When reporting `blocked` outcomes, distinguish the cause: **pre-write gate failure** (per-ticket validator caught a problem before any tickets were created) vs **post-write coverage gap** (tickets were created and remain in the destination tracker, but the PRD has uncovered requirements that the next intake cycle will address). Both result in the `blocked` label, but the implication for product is different — coverage gaps mean some tickets are already real and product should not re-author the PRD from scratch.
55
55
 
56
- If all PRDs ended in the `ticketed` label with coverage `COMPLETE`, mention that the next step is for product to monitor the created tickets and apply the configured `shipped` label after delivery. If any are `COMPLETE_WITH_SCOPE_CREEP`, point that out so product can review the flagged tickets.
56
+ If all PRDs ended in the `ticketed` label with coverage `COMPLETE`, mention that the next step is for product to monitor the created tickets and apply the configured `shipped` label after delivery, then run `/lisa:verify-prd` to empirically verify the shipped product against the PRD and move it to `verified` (or back to `blocked` with linked fix issues on failure). If any are `COMPLETE_WITH_SCOPE_CREEP`, point that out so product can review the flagged tickets.
57
57
 
58
58
  ## Rules
59
59
 
60
60
  - **Never run a cycle without an explicit scope.** Side effects are too high to default.
61
- - **Never modify the lifecycle**: only `ready → in_review → blocked|ticketed`. Never touch the `draft` or `shipped` labels. Never invent new labels.
61
+ - **Never modify the lifecycle**: only `ready → in_review → blocked|ticketed`. Never touch the `draft`, `shipped`, or `verified` labels (`shipped` is owned by the intake rollup phase; `verified` is owned by `/lisa:verify-prd`). Never invent new labels.
62
62
  - **Never write destination tickets directly.** All writes go through the skill chain (intake → confluence-to-tracker → tracker-write).
63
63
  - **Never edit a PRD's body.** Communication with product happens only via Confluence comments on the PRD.
64
64
  - **Never start a second cycle while one is in flight against the same scope.** Serial execution; the scheduling layer is responsible for not double-firing.
@@ -17,7 +17,7 @@ You are a PRD intake agent. Your single job is to run one intake cycle against t
17
17
 
18
18
  This agent is the GitHub counterpart of `notion-prd-intake`, `confluence-prd-intake`, and `linear-prd-intake`. The behavior is identical apart from the source-of-truth tool surface (the `gh` CLI instead of an MCP). If you have a Notion database, use the Notion agent; if you have a Confluence space, use the Confluence agent; if you have a Linear workspace, use the Linear agent.
19
19
 
20
- PRD label role names (`ready`, `in_review`, `blocked`, `ticketed`, `shipped`) are resolved from `.lisa.config.json` `github.labels.prd.*` by the `github-prd-intake` skill. The defaults match the legacy hardcoded names (`prd-ready`, `prd-in-review`, `prd-blocked`, `prd-ticketed`, `prd-shipped`).
20
+ PRD label role names (`ready`, `in_review`, `blocked`, `ticketed`, `shipped`, `verified`) are resolved from `.lisa.config.json` `github.labels.prd.*` by the `github-prd-intake` skill. The defaults match the legacy hardcoded names (`prd-ready`, `prd-in-review`, `prd-blocked`, `prd-ticketed`, `prd-shipped`, `prd-verified`). The full PRD lifecycle is `draft → ready → in_review → blocked | ticketed → shipped → verified`; this agent only ever drives `ready → in_review → blocked | ticketed`. The `shipped` rollup is owned by the intake skill's rollup phase, and `verified` is set by `/lisa:verify-prd` after empirical PRD-level acceptance — never by this agent.
21
21
 
22
22
  ## Confirmation policy
23
23
 
@@ -53,12 +53,12 @@ After a successful cycle, if any PRDs ended in the `blocked` label, mention to t
53
53
 
54
54
  When reporting `blocked` outcomes, distinguish the cause: **pre-write gate failure** (per-ticket validator caught a problem before any tickets were created) vs **post-write coverage gap** (tickets were created and remain in the destination tracker, but the PRD has uncovered requirements that the next intake cycle will address). Both result in the `blocked` label, but the implication for product is different — coverage gaps mean some tickets are already real and product should not re-author the PRD from scratch.
55
55
 
56
- If all PRDs ended in the `ticketed` label with coverage `COMPLETE`, mention that the next step is for product to monitor the created tickets and apply the configured `shipped` label after delivery. If any are `COMPLETE_WITH_SCOPE_CREEP`, point that out so product can review the flagged tickets.
56
+ If all PRDs ended in the `ticketed` label with coverage `COMPLETE`, mention that the next step is for product to monitor the created tickets and apply the configured `shipped` label after delivery, then run `/lisa:verify-prd` to empirically verify the shipped product against the PRD and move it to `verified` (or back to `blocked` with linked fix issues on failure). If any are `COMPLETE_WITH_SCOPE_CREEP`, point that out so product can review the flagged tickets.
57
57
 
58
58
  ## Rules
59
59
 
60
60
  - **Never run a cycle without an explicit repo.** Side effects are too high to default.
61
- - **Never modify the lifecycle**: only `ready → in_review → blocked|ticketed`. Never touch the `draft` or `shipped` labels. Never invent new labels.
61
+ - **Never modify the lifecycle**: only `ready → in_review → blocked|ticketed`. Never touch the `draft`, `shipped`, or `verified` labels (`shipped` is owned by the intake rollup phase; `verified` is owned by `/lisa:verify-prd`). Never invent new labels.
62
62
  - **Never write destination tickets directly.** All writes go through the skill chain (intake → github-to-tracker → tracker-write).
63
63
  - **Never edit a PRD's body.** Communication with product happens only via comments on the PRD issue.
64
64
  - **Never close, archive, or repurpose a PRD issue.** Even after the `ticketed` label is applied, the issue stays open and labeled `ticketed` until product flips it to the configured `shipped` label.
@@ -17,7 +17,7 @@ You are a PRD intake agent. Your single job is to run one intake cycle against t
17
17
 
18
18
  This agent is the Linear counterpart of `notion-prd-intake` and `confluence-prd-intake`. The behavior is identical apart from the source-of-truth tool surface and one structural difference (clarifying comments land on a sentinel feedback issue under each project, not on the project page itself, because Linear's MCP doesn't expose project-level comments). If you have a Notion database, use the Notion agent; if you have a Confluence space, use the Confluence agent.
19
19
 
20
- PRD label role names (`ready`, `in_review`, `blocked`, `ticketed`, `shipped`, `sentinel`) are resolved from `.lisa.config.json` `linear.labels.prd.*` by the `linear-prd-intake` skill. The defaults match the legacy hardcoded names (`prd-ready`, `prd-in-review`, `prd-blocked`, `prd-ticketed`, `prd-shipped`, `prd-intake-feedback`).
20
+ PRD label role names (`ready`, `in_review`, `blocked`, `ticketed`, `shipped`, `verified`, `sentinel`) are resolved from `.lisa.config.json` `linear.labels.prd.*` by the `linear-prd-intake` skill. The defaults match the legacy hardcoded names (`prd-ready`, `prd-in-review`, `prd-blocked`, `prd-ticketed`, `prd-shipped`, `prd-verified`, `prd-intake-feedback`). The full PRD lifecycle is `draft → ready → in_review → blocked | ticketed → shipped → verified`; this agent only ever drives `ready → in_review → blocked | ticketed`. The `shipped` rollup is owned by the intake skill's rollup phase, and `verified` is set by `/lisa:verify-prd` after empirical PRD-level acceptance — never by this agent.
21
21
 
22
22
  ## Confirmation policy
23
23
 
@@ -53,12 +53,12 @@ After a successful cycle, if any PRDs ended in the `blocked` label, mention to t
53
53
 
54
54
  When reporting `blocked` outcomes, distinguish the cause: **pre-write gate failure** (per-ticket validator caught a problem before any tickets were created) vs **post-write coverage gap** (tickets were created and remain in the destination tracker, but the PRD has uncovered requirements that the next intake cycle will address). Both result in the `blocked` label, but the implication for product is different — coverage gaps mean some tickets are already real and product should not re-author the PRD from scratch.
55
55
 
56
- If all PRDs ended in the `ticketed` label with coverage `COMPLETE`, mention that the next step is for product to monitor the created tickets and apply the configured `shipped` label after delivery. If any are `COMPLETE_WITH_SCOPE_CREEP`, point that out so product can review the flagged tickets.
56
+ If all PRDs ended in the `ticketed` label with coverage `COMPLETE`, mention that the next step is for product to monitor the created tickets and apply the configured `shipped` label after delivery, then run `/lisa:verify-prd` to empirically verify the shipped product against the PRD and move it to `verified` (or back to `blocked` with linked fix issues on failure). If any are `COMPLETE_WITH_SCOPE_CREEP`, point that out so product can review the flagged tickets.
57
57
 
58
58
  ## Rules
59
59
 
60
60
  - **Never run a cycle without an explicit scope.** Side effects are too high to default.
61
- - **Never modify the lifecycle**: only `ready → in_review → blocked|ticketed`. Never touch the `draft` or `shipped` labels. Never invent new labels.
61
+ - **Never modify the lifecycle**: only `ready → in_review → blocked|ticketed`. Never touch the `draft`, `shipped`, or `verified` labels (`shipped` is owned by the intake rollup phase; `verified` is owned by `/lisa:verify-prd`). Never invent new labels.
62
62
  - **Never write destination tickets directly.** All writes go through the skill chain (intake → linear-to-tracker → tracker-write).
63
63
  - **Never edit a project's description or any attached Linear document.** Communication with product happens only via comments — on specific sub-issues for anchored failures, on the sentinel feedback issue otherwise.
64
64
  - **Never close, archive, or repurpose the sentinel feedback issue.** It is reused across cycles; its longevity is the audit trail.
@@ -15,7 +15,7 @@ skills:
15
15
 
16
16
  You are a PRD intake agent. Your single job is to run one intake cycle against the Notion PRD database whose URL is given to you, then report what happened.
17
17
 
18
- Status role names (`draft`, `ready`, `in_review`, `blocked`, `ticketed`, `shipped`) are resolved from `.lisa.config.json` `notion.values.*` by the `notion-prd-intake` skill. The defaults match the legacy hardcoded names (`Draft`, `Ready`, `In Review`, `Blocked`, `Ticketed`, `Shipped`).
18
+ Status role names (`draft`, `ready`, `in_review`, `blocked`, `ticketed`, `shipped`, `verified`) are resolved from `.lisa.config.json` `notion.values.*` by the `notion-prd-intake` skill. The defaults match the legacy hardcoded names (`Draft`, `Ready`, `In Review`, `Blocked`, `Ticketed`, `Shipped`, `Verified`). The full PRD lifecycle is `draft → ready → in_review → blocked | ticketed → shipped → verified`; this agent only ever drives `ready → in_review → blocked | ticketed`. The `shipped` rollup is owned by the intake skill's rollup phase, and `verified` is set by `/lisa:verify-prd` after empirical PRD-level acceptance — never by this agent.
19
19
 
20
20
  ## Confirmation policy
21
21
 
@@ -51,12 +51,12 @@ After a successful cycle, if any PRDs ended in the `blocked` status, mention to
51
51
 
52
52
  When reporting `blocked` outcomes, distinguish the cause: **pre-write gate failure** (per-ticket validator caught a problem before any tickets were created) vs **post-write coverage gap** (tickets were created and remain in the destination tracker, but the PRD has uncovered requirements that the next intake cycle will address). Both result in the `blocked` status, but the implication for product is different — coverage gaps mean some tickets are already real and product should not re-author the PRD from scratch.
53
53
 
54
- If all PRDs ended in the `ticketed` status with coverage `COMPLETE`, mention that the next step is for product to monitor the created tickets and flip the PRDs to the configured `shipped` status after delivery. If any are `COMPLETE_WITH_SCOPE_CREEP`, point that out so product can review the flagged tickets.
54
+ If all PRDs ended in the `ticketed` status with coverage `COMPLETE`, mention that the next step is for product to monitor the created tickets and flip the PRDs to the configured `shipped` status after delivery, then run `/lisa:verify-prd` to empirically verify the shipped product against the PRD and move it to `verified` (or back to `blocked` with linked fix issues on failure). If any are `COMPLETE_WITH_SCOPE_CREEP`, point that out so product can review the flagged tickets.
55
55
 
56
56
  ## Rules
57
57
 
58
58
  - **Never run a cycle without an explicit database URL.** Side effects are too high to default.
59
- - **Never modify the lifecycle**: only `ready → in_review → blocked|ticketed`. Never touch `draft` or `shipped`. Never invent new status values.
59
+ - **Never modify the lifecycle**: only `ready → in_review → blocked|ticketed`. Never touch `draft`, `shipped`, or `verified` (`shipped` is owned by the intake rollup phase; `verified` is owned by `/lisa:verify-prd`). Never invent new status values.
60
60
  - **Never write destination tickets directly.** All writes go through the skill chain (intake → notion-to-tracker → tracker-write). Bypassing this skips quality gates.
61
61
  - **Never edit a PRD's body.** Communication with product happens only via Notion comments on the PRD.
62
62
  - **Never start a second cycle while one is in flight against the same database.** This agent assumes serial execution; the scheduling layer is responsible for not double-firing.
@@ -73,12 +73,32 @@ Where a vendor's terminal predicate references the build-status `done` role (Git
73
73
 
74
74
  When **all required** generated top-level children are terminal, the PRD rolls up to its terminal PRD-lifecycle state and, where configured, is closed/archived:
75
75
 
76
- 1. **Transition to `shipped`.** Set the PRD to the configured `shipped` role (`config-resolution` PRD-lifecycle roles: `prd-shipped` label for GitHub/Linear, `Shipped` status for Notion, the shipped parent page for Confluence). The PRD lifecycle is `ready → in_review → (blocked | ticketed) → shipped`; rollup performs the `ticketed → shipped` hop only, and only on the all-terminal condition.
76
+ 1. **Transition to `shipped`.** Set the PRD to the configured `shipped` role (`config-resolution` PRD-lifecycle roles: `prd-shipped` label for GitHub/Linear, `Shipped` status for Notion, the shipped parent page for Confluence). The PRD lifecycle is `ready → in_review → (blocked | ticketed) → shipped → verified`; rollup performs the `ticketed → shipped` hop only, and only on the all-terminal condition. The subsequent `shipped → verified` (pass) / `shipped → blocked` (fail) hops are owned by PRD-level verification (`/lisa:verify-prd`), **not** by this rollup — see "PRD-level verification vs ticket verification" below.
77
77
  2. **Config-gated close/archive.** When the source tool supports closure/archival *and* the project configures it, also close (GitHub: close the issue; Linear: move to a closed/archived state; JIRA: transition to Done; Confluence/Notion: archive where supported). Closure is **gated on configuration** via `prd.rollup.closeOnShipped` (`config-resolution`): when false (the default), the PRD is set to `shipped` but left open for a human to close; when true, rollup closes/archives it after the `shipped` transition. Never close a PRD before all generated top-level work is terminal (PRD #525 non-goal).
78
78
  3. **Partial completion is a no-op + report.** If only some required children are terminal, leave the PRD in its current state and report the incomplete/blocked child set. Do not advance, do not close.
79
79
 
80
80
  The PRD never advances to `shipped` on its own authority — it is **derived** from the generated-top-level-child set, exactly as a container's state is derived from its leaves in `leaf-only-lifecycle`.
81
81
 
82
+ ## PRD-level verification vs ticket verification
83
+
84
+ `shipped` and `verified` are two different terminal facts about a PRD, and they are established by two different flows. Keeping them distinct is the whole point of the `verified` state:
85
+
86
+ | | `shipped` | `verified` |
87
+ |---|---|---|
88
+ | **Meaning** | All generated top-level work is terminal — the work graph is *complete* | The shipped product has been *empirically checked against the PRD itself* |
89
+ | **Established by** | This rollup (`ticketed → shipped`), derived from child-ticket state | `/lisa:verify-prd`, the initiative-level acceptance gate |
90
+ | **Evidence** | The terminal-child set (closed issues / done labels / completed states) | Spec-conformance against the PRD's requirements + empirical proof (browser/computer use, API/CLI/DB checks, screenshots, logs) |
91
+ | **Ownership** | Set by the intake rollup phase; product may also set by hand | Product-owned, like `draft` and `shipped`; set only by `/lisa:verify-prd` |
92
+
93
+ `shipped` is **necessary but not sufficient** for `verified`: a PRD can have every ticket closed while still missing a requirement, diverging from its acceptance criteria, or failing a real user workflow. `verified` is what proves the shipped product actually matches the PRD.
94
+
95
+ **`/lisa:verify` (one work item) vs `/lisa:verify-prd` (the whole initiative).** These are deliberately separate scopes:
96
+
97
+ - **`/lisa:verify`** 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. It is what drives a build ticket to its `done` state; it operates at the *leaf/build* level (see `leaf-only-lifecycle`). It does **not** read the PRD or judge initiative-level acceptance, and it is **not** replaced by PRD verification.
98
+ - **`/lisa:verify-prd`** is the **initiative-level acceptance gate**. It runs *after* the PRD is `shipped` (all generated top-level work terminal), reads the PRD and its generated child set, confirms the children are terminal, then runs spec-conformance against the original PRD requirements plus empirical verification of the shipped surface. On pass it transitions the PRD `shipped → verified` and posts evidence; on fail it transitions the PRD `shipped → blocked`, posts a product-readable failure report, and creates linked fix issues for the missing or incorrect behavior. Re-runs are idempotent (no duplicate evidence, fix issues, or lifecycle transitions).
99
+
100
+ **No extra failure states.** A failed PRD verification reuses the existing `blocked` role (`shipped → blocked`) rather than introducing a `prd-verifying` or `prd-verification-failed` state — the lifecycle stays intentionally small. `verified` is terminal and product-owned (like `draft` and `shipped`); a PRD is never moved to `verified` solely because its tickets are closed, and a PRD is never closed/archived before verification has passed. The vendor maps for the `verified` role (`prd-verified` label for GitHub/Linear, `Verified` status for Notion, the verified parent page for Confluence) live in the `config-resolution` rule.
101
+
82
102
  ## Idempotency dedupe key
83
103
 
84
104
  Linking and rollup MUST be safe to re-run: re-running intake, backlink, or rollup never produces duplicate child links, duplicate sub-issues, duplicate generated-work entries, or a double `shipped` transition (PRD #525: "Re-running backlink or rollup is idempotent").
@@ -90,6 +110,8 @@ The dedupe key is **child-ref identity** — the stable, vendor-native identifie
90
110
  - **JIRA** — the issue key (e.g. `PROJ-123`).
91
111
  - **Confluence / Notion** — the destination ticket ref recorded in the generated-work entry (the entry is keyed by that ref, not by list position).
92
112
 
113
+ **Match by stable ref, never by title.** Identity is the child-ref above and *only* the child-ref — never the child's title, summary, or any other mutable field. A child whose **title changed but whose ref is unchanged is the same child**: re-running linking/backlink matches it by ref, updates the displayed title in place, and does **not** create a second link or a duplicate generated-work entry. Conversely, two distinct refs are two distinct children even if their titles happen to be identical. Title-based matching would both miss a renamed child (duplicating it) and falsely collapse two same-named children, so it is never used as the dedupe key (PRD #525: "Dedupe matches by stable ref not title").
114
+
93
115
  Apply it as follows:
94
116
 
95
117
  - **Linking** is keyed by child-ref: before adding a native child link or a documented generated-work entry, check whether that exact child-ref is already linked/listed; if so, it's a no-op. The documented generated-work section is **regenerated** from the current child set on each run rather than appended, so re-planning never accumulates stale or duplicate entries (the same regenerate-don't-append discipline `prd-backlink` already uses for its `## Tickets` section).
@@ -102,10 +102,14 @@ The only legitimate reasons to stop early:
102
102
  The Confluence PRD lifecycle is encoded as **parent-page placement** — Confluence has no native status field, and scoped API tokens cannot write labels. Each lifecycle role corresponds to a dedicated parent page in the project's Confluence space:
103
103
 
104
104
  ```text
105
- draft → ready → in_review → blocked | ticketed → shipped
106
- (product) (us) (us) (product)
105
+ draft → ready → in_review → blocked | ticketed → shipped → verified
106
+ (product) (us) (us) (product) (product)
107
107
  ```
108
108
 
109
+ (Each role corresponds to a dedicated parent page; the `verified` parent is resolved from `confluence.parents.verified`.)
110
+
111
+ `verified` is the terminal state after `shipped`: it means the shipped product has been empirically checked against the PRD (set by `/lisa:verify-prd`, not by this intake skill). A failed post-ship verification re-parents back under `blocked` rather than introducing a separate `verifying` / `verification-failed` parent. Like `draft` and `shipped`, `verified` is **product-owned** — this intake skill never re-parents a PRD into or out of the `verified` parent. See the "PRD-level verification vs ticket verification" section of the `prd-lifecycle-rollup` rule.
112
+
109
113
  A PRD's current state is determined entirely by which lifecycle parent it sits under. Re-parenting is the transition.
110
114
 
111
115
  This skill transitions:
@@ -116,7 +120,7 @@ This skill transitions:
116
120
  - `ticketed` → `blocked` (post-write coverage gaps from Phase 3e)
117
121
  - `ticketed` → `shipped` (PRD closure rollup, Phase 3f — only when **all** generated top-level children are terminal)
118
122
 
119
- It never re-parents PRDs into or out of the `draft` parentthat parent is owned by product. The `shipped` parent is set by this skill's **rollup phase (3f)** when, and only when, the PRD's generated top-level work is all terminal — per the `prd-lifecycle-rollup` rule; product may also re-parent there by hand. Rollup never advances a PRD to `shipped` on partial completion, and never archives a PRD page unless `confluence.rollup.closeOnShipped` is configured `true` (default `false` → re-parent under `shipped`, leave the page active).
123
+ It never re-parents PRDs into or out of the `draft` or `verified` parents those parents are owned by product (`verified` is set by `/lisa:verify-prd` after empirical PRD-level acceptance). The `shipped` parent is set by this skill's **rollup phase (3f)** when, and only when, the PRD's generated top-level work is all terminal — per the `prd-lifecycle-rollup` rule; product may also re-parent there by hand. Rollup never advances a PRD to `shipped` on partial completion, and never archives a PRD page unless `confluence.rollup.closeOnShipped` is configured `true` (default `false` → re-parent under `shipped`, leave the page active).
120
124
 
121
125
  A "transition" means: update the PRD's `parentId` to the new role's parent-page id via `lisa:atlassian-access` `operation: write-page payload: { id, parentId, title, version: { number: <next> } }`. The v2 PUT endpoint requires the next version number and the page title in the payload; the body content is not strictly required for a re-parent-only edit, but some Atlassian deployments reject PUTs without a body. The skill MUST therefore GET the page first via `read-page`, capture title + current version + current body, then PUT with `parentId` swapped and `version.number` bumped — preserving body content is non-negotiable, this skill never edits PRD content. See `transition_prd` helper in Phase 3a for the canonical implementation.
122
126
 
@@ -63,11 +63,13 @@ The only legitimate reasons to stop early:
63
63
  The PRD lifecycle is encoded as **issue labels**:
64
64
 
65
65
  ```text
66
- draft → ready → in_review → blocked | ticketed → shipped
67
- (product) (us) (us) (product)
66
+ draft → ready → in_review → blocked | ticketed → shipped → verified
67
+ (product) (us) (us) (product) (product)
68
68
  ```
69
69
 
70
- (Defaults: `prd-draft` / `prd-ready` / `prd-in-review` / `prd-blocked` / `prd-ticketed` / `prd-shipped`.)
70
+ (Defaults: `prd-draft` / `prd-ready` / `prd-in-review` / `prd-blocked` / `prd-ticketed` / `prd-shipped` / `prd-verified`.)
71
+
72
+ `verified` is the terminal state after `shipped`: it means the shipped product has been empirically checked against the PRD (set by `/lisa:verify-prd`, not by this intake skill). A failed post-ship verification reuses `blocked` rather than introducing a separate `verifying` / `verification-failed` state. Like `draft` and `shipped`, `verified` is **product-owned** — this intake skill never sets, clears, or otherwise touches it. See the "PRD-level verification vs ticket verification" section of the `prd-lifecycle-rollup` rule.
71
73
 
72
74
  Exactly one of these labels is expected on a PRD issue at any time.
73
75
 
@@ -79,7 +81,7 @@ This skill transitions:
79
81
  - `$TICKETED` → `$BLOCKED` (post-write coverage gaps from Phase 3e)
80
82
  - `$TICKETED` → `$SHIPPED` (PRD closure rollup, Phase 3f — only when **all** generated top-level children are terminal)
81
83
 
82
- The `draft` label is owned by product and is never touched here. The `shipped` label is set by this skill's **rollup phase (3f)** when, and only when, the PRD's generated top-level work is all terminal — per the `prd-lifecycle-rollup` rule; product may also set it by hand. Rollup never advances a PRD to `shipped` on partial completion, and never closes a PRD issue unless `github.labels.prd.rollup.closeOnShipped` is configured `true` (default `false` → set `shipped`, leave open).
84
+ The `draft` and `verified` labels are owned by product and are never touched here (`verified` is set by `/lisa:verify-prd` after empirical PRD-level acceptance). The `shipped` label is set by this skill's **rollup phase (3f)** when, and only when, the PRD's generated top-level work is all terminal — per the `prd-lifecycle-rollup` rule; product may also set it by hand. Rollup never advances a PRD to `shipped` on partial completion, and never closes a PRD issue unless `github.labels.prd.rollup.closeOnShipped` is configured `true` (default `false` → set `shipped`, leave open).
83
85
 
84
86
  A "transition" means: remove the old lifecycle label and add the new one (`gh issue edit <num> --remove-label <old> --add-label <new>`). The skill MUST verify exactly one lifecycle label is present after the update.
85
87
 
@@ -75,11 +75,13 @@ The only legitimate reasons to stop early:
75
75
  The Linear PRD lifecycle is encoded as **project labels** (we deliberately do NOT key off Linear's native project state, since project state is product's day-to-day signal and we don't want to fight it). Exactly one of these labels is expected on a project at any time:
76
76
 
77
77
  ```text
78
- draft → ready → in_review → blocked | ticketed → shipped
79
- (product) (us) (us) (product)
78
+ draft → ready → in_review → blocked | ticketed → shipped → verified
79
+ (product) (us) (us) (product) (product)
80
80
  ```
81
81
 
82
- (Defaults: `prd-draft` / `prd-ready` / `prd-in-review` / `prd-blocked` / `prd-ticketed` / `prd-shipped`.)
82
+ (Defaults: `prd-draft` / `prd-ready` / `prd-in-review` / `prd-blocked` / `prd-ticketed` / `prd-shipped` / `prd-verified`.)
83
+
84
+ `verified` is the terminal state after `shipped`: it means the shipped product has been empirically checked against the PRD (set by `/lisa:verify-prd`, not by this intake skill). A failed post-ship verification reuses `blocked` rather than introducing a separate `verifying` / `verification-failed` state. Like `draft` and `shipped`, `verified` is **product-owned** — this intake skill never sets, clears, or otherwise touches it. See the "PRD-level verification vs ticket verification" section of the `prd-lifecycle-rollup` rule.
83
85
 
84
86
  This skill transitions:
85
87
 
@@ -89,7 +91,7 @@ This skill transitions:
89
91
  - `$TICKETED` → `$BLOCKED` (post-write coverage gaps from Phase 3e)
90
92
  - `$TICKETED` → `$SHIPPED` (PRD closure rollup, Phase 3f — only when **all** generated top-level children are terminal)
91
93
 
92
- It never touches the `draft` labelthat label is owned by product. The `shipped` label is set by this skill's **rollup phase (3f)** when, and only when, the PRD project's generated top-level work is all terminal — per the `prd-lifecycle-rollup` rule; product may also set it by hand. Rollup never advances a PRD to `shipped` on partial completion, and never archives a PRD project unless `linear.labels.prd.rollup.closeOnShipped` is configured `true` (default `false` → set `shipped`, leave the project active).
94
+ It never touches the `draft` or `verified` labels those labels are owned by product (`verified` is set by `/lisa:verify-prd` after empirical PRD-level acceptance). The `shipped` label is set by this skill's **rollup phase (3f)** when, and only when, the PRD project's generated top-level work is all terminal — per the `prd-lifecycle-rollup` rule; product may also set it by hand. Rollup never advances a PRD to `shipped` on partial completion, and never archives a PRD project unless `linear.labels.prd.rollup.closeOnShipped` is configured `true` (default `false` → set `shipped`, leave the project active).
93
95
 
94
96
  A "transition" means: remove the old lifecycle label and add the new one in a single `save_project` call (passing the full new label set in the `labels` array). The skill MUST verify that exactly one lifecycle label exists on the project after the update — having two simultaneously breaks idempotency.
95
97
 
@@ -76,11 +76,15 @@ The only legitimate reasons to stop early:
76
76
  The PRD database has a status property (configurable via `notion.statusProperty`, default `Status`) whose value drives this skill:
77
77
 
78
78
  ```text
79
- draft → ready → in_review → blocked | ticketed → shipped
80
- (product) (us) (us) (product)
79
+ draft → ready → in_review → blocked | ticketed → shipped → verified
80
+ (product) (us) (us) (product) (product)
81
81
  ```
82
82
 
83
- This skill transitions `ready → in_review`, then `in_review blocked` or `in_review ticketed`, then (via the rollup phase 3f) `ticketed → shipped`. It never touches `draft` that status is owned by product. The `shipped` status is set by this skill's **rollup phase (3f)** when, and only when, the PRD's generated top-level work is all terminal — per the `prd-lifecycle-rollup` rule; product may also set it by hand. Rollup never advances a PRD to `shipped` on partial completion, and never archives a PRD page unless `notion.rollup.closeOnShipped` is configured `true` (default `false` → set `$SHIPPED`, leave the page active).
83
+ (Default status values: `Draft` / `Ready` / `In Review` / `Blocked` / `Ticketed` / `Shipped` / `Verified`.)
84
+
85
+ `verified` is the terminal status after `shipped`: it means the shipped product has been empirically checked against the PRD (set by `/lisa:verify-prd`, not by this intake skill). A failed post-ship verification reuses `blocked` rather than introducing a separate `verifying` / `verification-failed` status. Like `draft` and `shipped`, `verified` is **product-owned** — this intake skill never sets, clears, or otherwise touches it. See the "PRD-level verification vs ticket verification" section of the `prd-lifecycle-rollup` rule.
86
+
87
+ This skill transitions `ready → in_review`, then `in_review → blocked` or `in_review → ticketed`, then (via the rollup phase 3f) `ticketed → shipped`. It never touches `draft` or `verified` — those statuses are owned by product (`verified` is set by `/lisa:verify-prd` after empirical PRD-level acceptance). The `shipped` status is set by this skill's **rollup phase (3f)** when, and only when, the PRD's generated top-level work is all terminal — per the `prd-lifecycle-rollup` rule; product may also set it by hand. Rollup never advances a PRD to `shipped` on partial completion, and never archives a PRD page unless `notion.rollup.closeOnShipped` is configured `true` (default `false` → set `$SHIPPED`, leave the page active).
84
88
 
85
89
  ## Phases
86
90
 
@@ -99,6 +99,7 @@ Rendering rules:
99
99
  - Sort epics by key (lexical). Sort stories within an epic by key. Sort sub-tasks within a story by key. Sort the unparented list by `(type, key)`. The same sort applies to the `lisa:gw` tokens (each token sits on its entry's line), so the machine-readable order is identical across runs.
100
100
  - The line `_Generated by ..._` is fixed text — does not include a timestamp. A timestamp would defeat the diff-equality check Debrief relies on.
101
101
  - Regenerate the whole section from the current ticket set on every run — **never append**. Dedupe is by **child-ref** (the token's `ref`, per the `prd-lifecycle-rollup` idempotency dedupe key): the same ticket set produces a byte-identical section with no duplicate entries, and re-running over an existing section is a no-op diff. A ticket present in a prior run but absent now simply does not reappear (stale links never accumulate).
102
+ - **Match by stable ref, never by title** (`prd-lifecycle-rollup` idempotency dedupe key). A child is identified by its `ref` token alone — a ticket whose `title` changed but whose `ref` is unchanged is the **same** entry: its displayed title is refreshed in place and it appears exactly once, never duplicated. Title is rendered, never matched on.
102
103
 
103
104
  ## Native parent linking (GitHub)
104
105
 
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-cdk",
3
- "version": "2.46.0",
3
+ "version": "2.47.1",
4
4
  "description": "AWS CDK-specific plugin",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-cdk",
3
- "version": "2.46.0",
3
+ "version": "2.47.1",
4
4
  "description": "AWS CDK-specific Lisa plugin.",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-expo",
3
- "version": "2.46.0",
3
+ "version": "2.47.1",
4
4
  "description": "Expo/React Native-specific skills, agents, rules, and MCP servers",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-expo",
3
- "version": "2.46.0",
3
+ "version": "2.47.1",
4
4
  "description": "Expo and React Native-specific skills, agents, rules, and MCP servers.",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-harper-fabric",
3
- "version": "2.46.0",
3
+ "version": "2.47.1",
4
4
  "description": "Harper/Fabric-specific rules for TypeScript component apps",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-harper-fabric",
3
- "version": "2.46.0",
3
+ "version": "2.47.1",
4
4
  "description": "Harper/Fabric-specific Lisa rules for TypeScript component apps.",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-nestjs",
3
- "version": "2.46.0",
3
+ "version": "2.47.1",
4
4
  "description": "NestJS-specific skills (GraphQL, TypeORM) and hooks (migration write-protection)",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-nestjs",
3
- "version": "2.46.0",
3
+ "version": "2.47.1",
4
4
  "description": "NestJS-specific skills and migration write-protection hooks.",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-openclaw",
3
- "version": "2.46.0",
3
+ "version": "2.47.1",
4
4
  "description": "Connect staff roles to Telegram or Slack via OpenClaw — facilitator/specialist hub-and-spoke routing and repo-coding topics, for Claude Code and Codex",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-openclaw",
3
- "version": "2.46.0",
3
+ "version": "2.47.1",
4
4
  "description": "Connect staff roles to Telegram or Slack via OpenClaw — facilitator/specialist hub-and-spoke routing and repo-coding topics, across Claude and Codex.",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-rails",
3
- "version": "2.46.0",
3
+ "version": "2.47.1",
4
4
  "description": "Ruby on Rails-specific hooks — RuboCop linting/formatting and ast-grep scanning on edit",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-rails",
3
- "version": "2.46.0",
3
+ "version": "2.47.1",
4
4
  "description": "Ruby on Rails-specific skills and hooks for RuboCop and ast-grep scanning on edit.",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-typescript",
3
- "version": "2.46.0",
3
+ "version": "2.47.1",
4
4
  "description": "TypeScript-specific hooks — Prettier formatting, ESLint linting, and ast-grep scanning on edit",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-typescript",
3
- "version": "2.46.0",
3
+ "version": "2.47.1",
4
4
  "description": "TypeScript-specific hooks for formatting, linting, and ast-grep scanning on edit.",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-wiki",
3
- "version": "2.46.0",
3
+ "version": "2.47.1",
4
4
  "description": "LLM Wiki — a distributable, git-native markdown knowledge base for Claude Code and Codex",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-wiki",
3
- "version": "2.46.0",
3
+ "version": "2.47.1",
4
4
  "description": "Distributable LLM Wiki kernel — ingest, query, lint, and maintain a git-native markdown knowledge base across Claude and Codex.",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -17,7 +17,7 @@ You are a PRD intake agent. Your single job is to run one intake cycle against t
17
17
 
18
18
  This agent is the Confluence counterpart of `notion-prd-intake`. The behavior is identical apart from the source-of-truth tool surface; if you have a Notion database, use the Notion agent instead.
19
19
 
20
- Label role names (`ready`, `in_review`, `blocked`, `ticketed`, `shipped`) are resolved from `.lisa.config.json` `confluence.labels.*` by the `confluence-prd-intake` skill. The defaults match the legacy hardcoded names (`prd-ready`, `prd-in-review`, `prd-blocked`, `prd-ticketed`, `prd-shipped`).
20
+ Label role names (`ready`, `in_review`, `blocked`, `ticketed`, `shipped`, `verified`) are resolved from `.lisa.config.json` `confluence.labels.*` (parent-page placement) by the `confluence-prd-intake` skill. The defaults match the legacy hardcoded names (`prd-ready`, `prd-in-review`, `prd-blocked`, `prd-ticketed`, `prd-shipped`, `prd-verified`). The full PRD lifecycle is `draft → ready → in_review → blocked | ticketed → shipped → verified`; this agent only ever drives `ready → in_review → blocked | ticketed`. The `shipped` rollup is owned by the intake skill's rollup phase, and `verified` is set by `/lisa:verify-prd` after empirical PRD-level acceptance — never by this agent.
21
21
 
22
22
  ## Confirmation policy
23
23
 
@@ -53,12 +53,12 @@ After a successful cycle, if any PRDs ended in the `blocked` label, mention to t
53
53
 
54
54
  When reporting `blocked` outcomes, distinguish the cause: **pre-write gate failure** (per-ticket validator caught a problem before any tickets were created) vs **post-write coverage gap** (tickets were created and remain in the destination tracker, but the PRD has uncovered requirements that the next intake cycle will address). Both result in the `blocked` label, but the implication for product is different — coverage gaps mean some tickets are already real and product should not re-author the PRD from scratch.
55
55
 
56
- If all PRDs ended in the `ticketed` label with coverage `COMPLETE`, mention that the next step is for product to monitor the created tickets and apply the configured `shipped` label after delivery. If any are `COMPLETE_WITH_SCOPE_CREEP`, point that out so product can review the flagged tickets.
56
+ If all PRDs ended in the `ticketed` label with coverage `COMPLETE`, mention that the next step is for product to monitor the created tickets and apply the configured `shipped` label after delivery, then run `/lisa:verify-prd` to empirically verify the shipped product against the PRD and move it to `verified` (or back to `blocked` with linked fix issues on failure). If any are `COMPLETE_WITH_SCOPE_CREEP`, point that out so product can review the flagged tickets.
57
57
 
58
58
  ## Rules
59
59
 
60
60
  - **Never run a cycle without an explicit scope.** Side effects are too high to default.
61
- - **Never modify the lifecycle**: only `ready → in_review → blocked|ticketed`. Never touch the `draft` or `shipped` labels. Never invent new labels.
61
+ - **Never modify the lifecycle**: only `ready → in_review → blocked|ticketed`. Never touch the `draft`, `shipped`, or `verified` labels (`shipped` is owned by the intake rollup phase; `verified` is owned by `/lisa:verify-prd`). Never invent new labels.
62
62
  - **Never write destination tickets directly.** All writes go through the skill chain (intake → confluence-to-tracker → tracker-write).
63
63
  - **Never edit a PRD's body.** Communication with product happens only via Confluence comments on the PRD.
64
64
  - **Never start a second cycle while one is in flight against the same scope.** Serial execution; the scheduling layer is responsible for not double-firing.
@@ -17,7 +17,7 @@ You are a PRD intake agent. Your single job is to run one intake cycle against t
17
17
 
18
18
  This agent is the GitHub counterpart of `notion-prd-intake`, `confluence-prd-intake`, and `linear-prd-intake`. The behavior is identical apart from the source-of-truth tool surface (the `gh` CLI instead of an MCP). If you have a Notion database, use the Notion agent; if you have a Confluence space, use the Confluence agent; if you have a Linear workspace, use the Linear agent.
19
19
 
20
- PRD label role names (`ready`, `in_review`, `blocked`, `ticketed`, `shipped`) are resolved from `.lisa.config.json` `github.labels.prd.*` by the `github-prd-intake` skill. The defaults match the legacy hardcoded names (`prd-ready`, `prd-in-review`, `prd-blocked`, `prd-ticketed`, `prd-shipped`).
20
+ PRD label role names (`ready`, `in_review`, `blocked`, `ticketed`, `shipped`, `verified`) are resolved from `.lisa.config.json` `github.labels.prd.*` by the `github-prd-intake` skill. The defaults match the legacy hardcoded names (`prd-ready`, `prd-in-review`, `prd-blocked`, `prd-ticketed`, `prd-shipped`, `prd-verified`). The full PRD lifecycle is `draft → ready → in_review → blocked | ticketed → shipped → verified`; this agent only ever drives `ready → in_review → blocked | ticketed`. The `shipped` rollup is owned by the intake skill's rollup phase, and `verified` is set by `/lisa:verify-prd` after empirical PRD-level acceptance — never by this agent.
21
21
 
22
22
  ## Confirmation policy
23
23
 
@@ -53,12 +53,12 @@ After a successful cycle, if any PRDs ended in the `blocked` label, mention to t
53
53
 
54
54
  When reporting `blocked` outcomes, distinguish the cause: **pre-write gate failure** (per-ticket validator caught a problem before any tickets were created) vs **post-write coverage gap** (tickets were created and remain in the destination tracker, but the PRD has uncovered requirements that the next intake cycle will address). Both result in the `blocked` label, but the implication for product is different — coverage gaps mean some tickets are already real and product should not re-author the PRD from scratch.
55
55
 
56
- If all PRDs ended in the `ticketed` label with coverage `COMPLETE`, mention that the next step is for product to monitor the created tickets and apply the configured `shipped` label after delivery. If any are `COMPLETE_WITH_SCOPE_CREEP`, point that out so product can review the flagged tickets.
56
+ If all PRDs ended in the `ticketed` label with coverage `COMPLETE`, mention that the next step is for product to monitor the created tickets and apply the configured `shipped` label after delivery, then run `/lisa:verify-prd` to empirically verify the shipped product against the PRD and move it to `verified` (or back to `blocked` with linked fix issues on failure). If any are `COMPLETE_WITH_SCOPE_CREEP`, point that out so product can review the flagged tickets.
57
57
 
58
58
  ## Rules
59
59
 
60
60
  - **Never run a cycle without an explicit repo.** Side effects are too high to default.
61
- - **Never modify the lifecycle**: only `ready → in_review → blocked|ticketed`. Never touch the `draft` or `shipped` labels. Never invent new labels.
61
+ - **Never modify the lifecycle**: only `ready → in_review → blocked|ticketed`. Never touch the `draft`, `shipped`, or `verified` labels (`shipped` is owned by the intake rollup phase; `verified` is owned by `/lisa:verify-prd`). Never invent new labels.
62
62
  - **Never write destination tickets directly.** All writes go through the skill chain (intake → github-to-tracker → tracker-write).
63
63
  - **Never edit a PRD's body.** Communication with product happens only via comments on the PRD issue.
64
64
  - **Never close, archive, or repurpose a PRD issue.** Even after the `ticketed` label is applied, the issue stays open and labeled `ticketed` until product flips it to the configured `shipped` label.
@@ -17,7 +17,7 @@ You are a PRD intake agent. Your single job is to run one intake cycle against t
17
17
 
18
18
  This agent is the Linear counterpart of `notion-prd-intake` and `confluence-prd-intake`. The behavior is identical apart from the source-of-truth tool surface and one structural difference (clarifying comments land on a sentinel feedback issue under each project, not on the project page itself, because Linear's MCP doesn't expose project-level comments). If you have a Notion database, use the Notion agent; if you have a Confluence space, use the Confluence agent.
19
19
 
20
- PRD label role names (`ready`, `in_review`, `blocked`, `ticketed`, `shipped`, `sentinel`) are resolved from `.lisa.config.json` `linear.labels.prd.*` by the `linear-prd-intake` skill. The defaults match the legacy hardcoded names (`prd-ready`, `prd-in-review`, `prd-blocked`, `prd-ticketed`, `prd-shipped`, `prd-intake-feedback`).
20
+ PRD label role names (`ready`, `in_review`, `blocked`, `ticketed`, `shipped`, `verified`, `sentinel`) are resolved from `.lisa.config.json` `linear.labels.prd.*` by the `linear-prd-intake` skill. The defaults match the legacy hardcoded names (`prd-ready`, `prd-in-review`, `prd-blocked`, `prd-ticketed`, `prd-shipped`, `prd-verified`, `prd-intake-feedback`). The full PRD lifecycle is `draft → ready → in_review → blocked | ticketed → shipped → verified`; this agent only ever drives `ready → in_review → blocked | ticketed`. The `shipped` rollup is owned by the intake skill's rollup phase, and `verified` is set by `/lisa:verify-prd` after empirical PRD-level acceptance — never by this agent.
21
21
 
22
22
  ## Confirmation policy
23
23
 
@@ -53,12 +53,12 @@ After a successful cycle, if any PRDs ended in the `blocked` label, mention to t
53
53
 
54
54
  When reporting `blocked` outcomes, distinguish the cause: **pre-write gate failure** (per-ticket validator caught a problem before any tickets were created) vs **post-write coverage gap** (tickets were created and remain in the destination tracker, but the PRD has uncovered requirements that the next intake cycle will address). Both result in the `blocked` label, but the implication for product is different — coverage gaps mean some tickets are already real and product should not re-author the PRD from scratch.
55
55
 
56
- If all PRDs ended in the `ticketed` label with coverage `COMPLETE`, mention that the next step is for product to monitor the created tickets and apply the configured `shipped` label after delivery. If any are `COMPLETE_WITH_SCOPE_CREEP`, point that out so product can review the flagged tickets.
56
+ If all PRDs ended in the `ticketed` label with coverage `COMPLETE`, mention that the next step is for product to monitor the created tickets and apply the configured `shipped` label after delivery, then run `/lisa:verify-prd` to empirically verify the shipped product against the PRD and move it to `verified` (or back to `blocked` with linked fix issues on failure). If any are `COMPLETE_WITH_SCOPE_CREEP`, point that out so product can review the flagged tickets.
57
57
 
58
58
  ## Rules
59
59
 
60
60
  - **Never run a cycle without an explicit scope.** Side effects are too high to default.
61
- - **Never modify the lifecycle**: only `ready → in_review → blocked|ticketed`. Never touch the `draft` or `shipped` labels. Never invent new labels.
61
+ - **Never modify the lifecycle**: only `ready → in_review → blocked|ticketed`. Never touch the `draft`, `shipped`, or `verified` labels (`shipped` is owned by the intake rollup phase; `verified` is owned by `/lisa:verify-prd`). Never invent new labels.
62
62
  - **Never write destination tickets directly.** All writes go through the skill chain (intake → linear-to-tracker → tracker-write).
63
63
  - **Never edit a project's description or any attached Linear document.** Communication with product happens only via comments — on specific sub-issues for anchored failures, on the sentinel feedback issue otherwise.
64
64
  - **Never close, archive, or repurpose the sentinel feedback issue.** It is reused across cycles; its longevity is the audit trail.
@@ -15,7 +15,7 @@ skills:
15
15
 
16
16
  You are a PRD intake agent. Your single job is to run one intake cycle against the Notion PRD database whose URL is given to you, then report what happened.
17
17
 
18
- Status role names (`draft`, `ready`, `in_review`, `blocked`, `ticketed`, `shipped`) are resolved from `.lisa.config.json` `notion.values.*` by the `notion-prd-intake` skill. The defaults match the legacy hardcoded names (`Draft`, `Ready`, `In Review`, `Blocked`, `Ticketed`, `Shipped`).
18
+ Status role names (`draft`, `ready`, `in_review`, `blocked`, `ticketed`, `shipped`, `verified`) are resolved from `.lisa.config.json` `notion.values.*` by the `notion-prd-intake` skill. The defaults match the legacy hardcoded names (`Draft`, `Ready`, `In Review`, `Blocked`, `Ticketed`, `Shipped`, `Verified`). The full PRD lifecycle is `draft → ready → in_review → blocked | ticketed → shipped → verified`; this agent only ever drives `ready → in_review → blocked | ticketed`. The `shipped` rollup is owned by the intake skill's rollup phase, and `verified` is set by `/lisa:verify-prd` after empirical PRD-level acceptance — never by this agent.
19
19
 
20
20
  ## Confirmation policy
21
21
 
@@ -51,12 +51,12 @@ After a successful cycle, if any PRDs ended in the `blocked` status, mention to
51
51
 
52
52
  When reporting `blocked` outcomes, distinguish the cause: **pre-write gate failure** (per-ticket validator caught a problem before any tickets were created) vs **post-write coverage gap** (tickets were created and remain in the destination tracker, but the PRD has uncovered requirements that the next intake cycle will address). Both result in the `blocked` status, but the implication for product is different — coverage gaps mean some tickets are already real and product should not re-author the PRD from scratch.
53
53
 
54
- If all PRDs ended in the `ticketed` status with coverage `COMPLETE`, mention that the next step is for product to monitor the created tickets and flip the PRDs to the configured `shipped` status after delivery. If any are `COMPLETE_WITH_SCOPE_CREEP`, point that out so product can review the flagged tickets.
54
+ If all PRDs ended in the `ticketed` status with coverage `COMPLETE`, mention that the next step is for product to monitor the created tickets and flip the PRDs to the configured `shipped` status after delivery, then run `/lisa:verify-prd` to empirically verify the shipped product against the PRD and move it to `verified` (or back to `blocked` with linked fix issues on failure). If any are `COMPLETE_WITH_SCOPE_CREEP`, point that out so product can review the flagged tickets.
55
55
 
56
56
  ## Rules
57
57
 
58
58
  - **Never run a cycle without an explicit database URL.** Side effects are too high to default.
59
- - **Never modify the lifecycle**: only `ready → in_review → blocked|ticketed`. Never touch `draft` or `shipped`. Never invent new status values.
59
+ - **Never modify the lifecycle**: only `ready → in_review → blocked|ticketed`. Never touch `draft`, `shipped`, or `verified` (`shipped` is owned by the intake rollup phase; `verified` is owned by `/lisa:verify-prd`). Never invent new status values.
60
60
  - **Never write destination tickets directly.** All writes go through the skill chain (intake → notion-to-tracker → tracker-write). Bypassing this skips quality gates.
61
61
  - **Never edit a PRD's body.** Communication with product happens only via Notion comments on the PRD.
62
62
  - **Never start a second cycle while one is in flight against the same database.** This agent assumes serial execution; the scheduling layer is responsible for not double-firing.
@@ -73,12 +73,32 @@ Where a vendor's terminal predicate references the build-status `done` role (Git
73
73
 
74
74
  When **all required** generated top-level children are terminal, the PRD rolls up to its terminal PRD-lifecycle state and, where configured, is closed/archived:
75
75
 
76
- 1. **Transition to `shipped`.** Set the PRD to the configured `shipped` role (`config-resolution` PRD-lifecycle roles: `prd-shipped` label for GitHub/Linear, `Shipped` status for Notion, the shipped parent page for Confluence). The PRD lifecycle is `ready → in_review → (blocked | ticketed) → shipped`; rollup performs the `ticketed → shipped` hop only, and only on the all-terminal condition.
76
+ 1. **Transition to `shipped`.** Set the PRD to the configured `shipped` role (`config-resolution` PRD-lifecycle roles: `prd-shipped` label for GitHub/Linear, `Shipped` status for Notion, the shipped parent page for Confluence). The PRD lifecycle is `ready → in_review → (blocked | ticketed) → shipped → verified`; rollup performs the `ticketed → shipped` hop only, and only on the all-terminal condition. The subsequent `shipped → verified` (pass) / `shipped → blocked` (fail) hops are owned by PRD-level verification (`/lisa:verify-prd`), **not** by this rollup — see "PRD-level verification vs ticket verification" below.
77
77
  2. **Config-gated close/archive.** When the source tool supports closure/archival *and* the project configures it, also close (GitHub: close the issue; Linear: move to a closed/archived state; JIRA: transition to Done; Confluence/Notion: archive where supported). Closure is **gated on configuration** via `prd.rollup.closeOnShipped` (`config-resolution`): when false (the default), the PRD is set to `shipped` but left open for a human to close; when true, rollup closes/archives it after the `shipped` transition. Never close a PRD before all generated top-level work is terminal (PRD #525 non-goal).
78
78
  3. **Partial completion is a no-op + report.** If only some required children are terminal, leave the PRD in its current state and report the incomplete/blocked child set. Do not advance, do not close.
79
79
 
80
80
  The PRD never advances to `shipped` on its own authority — it is **derived** from the generated-top-level-child set, exactly as a container's state is derived from its leaves in `leaf-only-lifecycle`.
81
81
 
82
+ ## PRD-level verification vs ticket verification
83
+
84
+ `shipped` and `verified` are two different terminal facts about a PRD, and they are established by two different flows. Keeping them distinct is the whole point of the `verified` state:
85
+
86
+ | | `shipped` | `verified` |
87
+ |---|---|---|
88
+ | **Meaning** | All generated top-level work is terminal — the work graph is *complete* | The shipped product has been *empirically checked against the PRD itself* |
89
+ | **Established by** | This rollup (`ticketed → shipped`), derived from child-ticket state | `/lisa:verify-prd`, the initiative-level acceptance gate |
90
+ | **Evidence** | The terminal-child set (closed issues / done labels / completed states) | Spec-conformance against the PRD's requirements + empirical proof (browser/computer use, API/CLI/DB checks, screenshots, logs) |
91
+ | **Ownership** | Set by the intake rollup phase; product may also set by hand | Product-owned, like `draft` and `shipped`; set only by `/lisa:verify-prd` |
92
+
93
+ `shipped` is **necessary but not sufficient** for `verified`: a PRD can have every ticket closed while still missing a requirement, diverging from its acceptance criteria, or failing a real user workflow. `verified` is what proves the shipped product actually matches the PRD.
94
+
95
+ **`/lisa:verify` (one work item) vs `/lisa:verify-prd` (the whole initiative).** These are deliberately separate scopes:
96
+
97
+ - **`/lisa:verify`** 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. It is what drives a build ticket to its `done` state; it operates at the *leaf/build* level (see `leaf-only-lifecycle`). It does **not** read the PRD or judge initiative-level acceptance, and it is **not** replaced by PRD verification.
98
+ - **`/lisa:verify-prd`** is the **initiative-level acceptance gate**. It runs *after* the PRD is `shipped` (all generated top-level work terminal), reads the PRD and its generated child set, confirms the children are terminal, then runs spec-conformance against the original PRD requirements plus empirical verification of the shipped surface. On pass it transitions the PRD `shipped → verified` and posts evidence; on fail it transitions the PRD `shipped → blocked`, posts a product-readable failure report, and creates linked fix issues for the missing or incorrect behavior. Re-runs are idempotent (no duplicate evidence, fix issues, or lifecycle transitions).
99
+
100
+ **No extra failure states.** A failed PRD verification reuses the existing `blocked` role (`shipped → blocked`) rather than introducing a `prd-verifying` or `prd-verification-failed` state — the lifecycle stays intentionally small. `verified` is terminal and product-owned (like `draft` and `shipped`); a PRD is never moved to `verified` solely because its tickets are closed, and a PRD is never closed/archived before verification has passed. The vendor maps for the `verified` role (`prd-verified` label for GitHub/Linear, `Verified` status for Notion, the verified parent page for Confluence) live in the `config-resolution` rule.
101
+
82
102
  ## Idempotency dedupe key
83
103
 
84
104
  Linking and rollup MUST be safe to re-run: re-running intake, backlink, or rollup never produces duplicate child links, duplicate sub-issues, duplicate generated-work entries, or a double `shipped` transition (PRD #525: "Re-running backlink or rollup is idempotent").
@@ -90,6 +110,8 @@ The dedupe key is **child-ref identity** — the stable, vendor-native identifie
90
110
  - **JIRA** — the issue key (e.g. `PROJ-123`).
91
111
  - **Confluence / Notion** — the destination ticket ref recorded in the generated-work entry (the entry is keyed by that ref, not by list position).
92
112
 
113
+ **Match by stable ref, never by title.** Identity is the child-ref above and *only* the child-ref — never the child's title, summary, or any other mutable field. A child whose **title changed but whose ref is unchanged is the same child**: re-running linking/backlink matches it by ref, updates the displayed title in place, and does **not** create a second link or a duplicate generated-work entry. Conversely, two distinct refs are two distinct children even if their titles happen to be identical. Title-based matching would both miss a renamed child (duplicating it) and falsely collapse two same-named children, so it is never used as the dedupe key (PRD #525: "Dedupe matches by stable ref not title").
114
+
93
115
  Apply it as follows:
94
116
 
95
117
  - **Linking** is keyed by child-ref: before adding a native child link or a documented generated-work entry, check whether that exact child-ref is already linked/listed; if so, it's a no-op. The documented generated-work section is **regenerated** from the current child set on each run rather than appended, so re-planning never accumulates stale or duplicate entries (the same regenerate-don't-append discipline `prd-backlink` already uses for its `## Tickets` section).
@@ -102,10 +102,14 @@ The only legitimate reasons to stop early:
102
102
  The Confluence PRD lifecycle is encoded as **parent-page placement** — Confluence has no native status field, and scoped API tokens cannot write labels. Each lifecycle role corresponds to a dedicated parent page in the project's Confluence space:
103
103
 
104
104
  ```text
105
- draft → ready → in_review → blocked | ticketed → shipped
106
- (product) (us) (us) (product)
105
+ draft → ready → in_review → blocked | ticketed → shipped → verified
106
+ (product) (us) (us) (product) (product)
107
107
  ```
108
108
 
109
+ (Each role corresponds to a dedicated parent page; the `verified` parent is resolved from `confluence.parents.verified`.)
110
+
111
+ `verified` is the terminal state after `shipped`: it means the shipped product has been empirically checked against the PRD (set by `/lisa:verify-prd`, not by this intake skill). A failed post-ship verification re-parents back under `blocked` rather than introducing a separate `verifying` / `verification-failed` parent. Like `draft` and `shipped`, `verified` is **product-owned** — this intake skill never re-parents a PRD into or out of the `verified` parent. See the "PRD-level verification vs ticket verification" section of the `prd-lifecycle-rollup` rule.
112
+
109
113
  A PRD's current state is determined entirely by which lifecycle parent it sits under. Re-parenting is the transition.
110
114
 
111
115
  This skill transitions:
@@ -116,7 +120,7 @@ This skill transitions:
116
120
  - `ticketed` → `blocked` (post-write coverage gaps from Phase 3e)
117
121
  - `ticketed` → `shipped` (PRD closure rollup, Phase 3f — only when **all** generated top-level children are terminal)
118
122
 
119
- It never re-parents PRDs into or out of the `draft` parentthat parent is owned by product. The `shipped` parent is set by this skill's **rollup phase (3f)** when, and only when, the PRD's generated top-level work is all terminal — per the `prd-lifecycle-rollup` rule; product may also re-parent there by hand. Rollup never advances a PRD to `shipped` on partial completion, and never archives a PRD page unless `confluence.rollup.closeOnShipped` is configured `true` (default `false` → re-parent under `shipped`, leave the page active).
123
+ It never re-parents PRDs into or out of the `draft` or `verified` parents those parents are owned by product (`verified` is set by `/lisa:verify-prd` after empirical PRD-level acceptance). The `shipped` parent is set by this skill's **rollup phase (3f)** when, and only when, the PRD's generated top-level work is all terminal — per the `prd-lifecycle-rollup` rule; product may also re-parent there by hand. Rollup never advances a PRD to `shipped` on partial completion, and never archives a PRD page unless `confluence.rollup.closeOnShipped` is configured `true` (default `false` → re-parent under `shipped`, leave the page active).
120
124
 
121
125
  A "transition" means: update the PRD's `parentId` to the new role's parent-page id via `lisa:atlassian-access` `operation: write-page payload: { id, parentId, title, version: { number: <next> } }`. The v2 PUT endpoint requires the next version number and the page title in the payload; the body content is not strictly required for a re-parent-only edit, but some Atlassian deployments reject PUTs without a body. The skill MUST therefore GET the page first via `read-page`, capture title + current version + current body, then PUT with `parentId` swapped and `version.number` bumped — preserving body content is non-negotiable, this skill never edits PRD content. See `transition_prd` helper in Phase 3a for the canonical implementation.
122
126
 
@@ -63,11 +63,13 @@ The only legitimate reasons to stop early:
63
63
  The PRD lifecycle is encoded as **issue labels**:
64
64
 
65
65
  ```text
66
- draft → ready → in_review → blocked | ticketed → shipped
67
- (product) (us) (us) (product)
66
+ draft → ready → in_review → blocked | ticketed → shipped → verified
67
+ (product) (us) (us) (product) (product)
68
68
  ```
69
69
 
70
- (Defaults: `prd-draft` / `prd-ready` / `prd-in-review` / `prd-blocked` / `prd-ticketed` / `prd-shipped`.)
70
+ (Defaults: `prd-draft` / `prd-ready` / `prd-in-review` / `prd-blocked` / `prd-ticketed` / `prd-shipped` / `prd-verified`.)
71
+
72
+ `verified` is the terminal state after `shipped`: it means the shipped product has been empirically checked against the PRD (set by `/lisa:verify-prd`, not by this intake skill). A failed post-ship verification reuses `blocked` rather than introducing a separate `verifying` / `verification-failed` state. Like `draft` and `shipped`, `verified` is **product-owned** — this intake skill never sets, clears, or otherwise touches it. See the "PRD-level verification vs ticket verification" section of the `prd-lifecycle-rollup` rule.
71
73
 
72
74
  Exactly one of these labels is expected on a PRD issue at any time.
73
75
 
@@ -79,7 +81,7 @@ This skill transitions:
79
81
  - `$TICKETED` → `$BLOCKED` (post-write coverage gaps from Phase 3e)
80
82
  - `$TICKETED` → `$SHIPPED` (PRD closure rollup, Phase 3f — only when **all** generated top-level children are terminal)
81
83
 
82
- The `draft` label is owned by product and is never touched here. The `shipped` label is set by this skill's **rollup phase (3f)** when, and only when, the PRD's generated top-level work is all terminal — per the `prd-lifecycle-rollup` rule; product may also set it by hand. Rollup never advances a PRD to `shipped` on partial completion, and never closes a PRD issue unless `github.labels.prd.rollup.closeOnShipped` is configured `true` (default `false` → set `shipped`, leave open).
84
+ The `draft` and `verified` labels are owned by product and are never touched here (`verified` is set by `/lisa:verify-prd` after empirical PRD-level acceptance). The `shipped` label is set by this skill's **rollup phase (3f)** when, and only when, the PRD's generated top-level work is all terminal — per the `prd-lifecycle-rollup` rule; product may also set it by hand. Rollup never advances a PRD to `shipped` on partial completion, and never closes a PRD issue unless `github.labels.prd.rollup.closeOnShipped` is configured `true` (default `false` → set `shipped`, leave open).
83
85
 
84
86
  A "transition" means: remove the old lifecycle label and add the new one (`gh issue edit <num> --remove-label <old> --add-label <new>`). The skill MUST verify exactly one lifecycle label is present after the update.
85
87
 
@@ -75,11 +75,13 @@ The only legitimate reasons to stop early:
75
75
  The Linear PRD lifecycle is encoded as **project labels** (we deliberately do NOT key off Linear's native project state, since project state is product's day-to-day signal and we don't want to fight it). Exactly one of these labels is expected on a project at any time:
76
76
 
77
77
  ```text
78
- draft → ready → in_review → blocked | ticketed → shipped
79
- (product) (us) (us) (product)
78
+ draft → ready → in_review → blocked | ticketed → shipped → verified
79
+ (product) (us) (us) (product) (product)
80
80
  ```
81
81
 
82
- (Defaults: `prd-draft` / `prd-ready` / `prd-in-review` / `prd-blocked` / `prd-ticketed` / `prd-shipped`.)
82
+ (Defaults: `prd-draft` / `prd-ready` / `prd-in-review` / `prd-blocked` / `prd-ticketed` / `prd-shipped` / `prd-verified`.)
83
+
84
+ `verified` is the terminal state after `shipped`: it means the shipped product has been empirically checked against the PRD (set by `/lisa:verify-prd`, not by this intake skill). A failed post-ship verification reuses `blocked` rather than introducing a separate `verifying` / `verification-failed` state. Like `draft` and `shipped`, `verified` is **product-owned** — this intake skill never sets, clears, or otherwise touches it. See the "PRD-level verification vs ticket verification" section of the `prd-lifecycle-rollup` rule.
83
85
 
84
86
  This skill transitions:
85
87
 
@@ -89,7 +91,7 @@ This skill transitions:
89
91
  - `$TICKETED` → `$BLOCKED` (post-write coverage gaps from Phase 3e)
90
92
  - `$TICKETED` → `$SHIPPED` (PRD closure rollup, Phase 3f — only when **all** generated top-level children are terminal)
91
93
 
92
- It never touches the `draft` labelthat label is owned by product. The `shipped` label is set by this skill's **rollup phase (3f)** when, and only when, the PRD project's generated top-level work is all terminal — per the `prd-lifecycle-rollup` rule; product may also set it by hand. Rollup never advances a PRD to `shipped` on partial completion, and never archives a PRD project unless `linear.labels.prd.rollup.closeOnShipped` is configured `true` (default `false` → set `shipped`, leave the project active).
94
+ It never touches the `draft` or `verified` labels those labels are owned by product (`verified` is set by `/lisa:verify-prd` after empirical PRD-level acceptance). The `shipped` label is set by this skill's **rollup phase (3f)** when, and only when, the PRD project's generated top-level work is all terminal — per the `prd-lifecycle-rollup` rule; product may also set it by hand. Rollup never advances a PRD to `shipped` on partial completion, and never archives a PRD project unless `linear.labels.prd.rollup.closeOnShipped` is configured `true` (default `false` → set `shipped`, leave the project active).
93
95
 
94
96
  A "transition" means: remove the old lifecycle label and add the new one in a single `save_project` call (passing the full new label set in the `labels` array). The skill MUST verify that exactly one lifecycle label exists on the project after the update — having two simultaneously breaks idempotency.
95
97
 
@@ -76,11 +76,15 @@ The only legitimate reasons to stop early:
76
76
  The PRD database has a status property (configurable via `notion.statusProperty`, default `Status`) whose value drives this skill:
77
77
 
78
78
  ```text
79
- draft → ready → in_review → blocked | ticketed → shipped
80
- (product) (us) (us) (product)
79
+ draft → ready → in_review → blocked | ticketed → shipped → verified
80
+ (product) (us) (us) (product) (product)
81
81
  ```
82
82
 
83
- This skill transitions `ready → in_review`, then `in_review blocked` or `in_review ticketed`, then (via the rollup phase 3f) `ticketed → shipped`. It never touches `draft` that status is owned by product. The `shipped` status is set by this skill's **rollup phase (3f)** when, and only when, the PRD's generated top-level work is all terminal — per the `prd-lifecycle-rollup` rule; product may also set it by hand. Rollup never advances a PRD to `shipped` on partial completion, and never archives a PRD page unless `notion.rollup.closeOnShipped` is configured `true` (default `false` → set `$SHIPPED`, leave the page active).
83
+ (Default status values: `Draft` / `Ready` / `In Review` / `Blocked` / `Ticketed` / `Shipped` / `Verified`.)
84
+
85
+ `verified` is the terminal status after `shipped`: it means the shipped product has been empirically checked against the PRD (set by `/lisa:verify-prd`, not by this intake skill). A failed post-ship verification reuses `blocked` rather than introducing a separate `verifying` / `verification-failed` status. Like `draft` and `shipped`, `verified` is **product-owned** — this intake skill never sets, clears, or otherwise touches it. See the "PRD-level verification vs ticket verification" section of the `prd-lifecycle-rollup` rule.
86
+
87
+ This skill transitions `ready → in_review`, then `in_review → blocked` or `in_review → ticketed`, then (via the rollup phase 3f) `ticketed → shipped`. It never touches `draft` or `verified` — those statuses are owned by product (`verified` is set by `/lisa:verify-prd` after empirical PRD-level acceptance). The `shipped` status is set by this skill's **rollup phase (3f)** when, and only when, the PRD's generated top-level work is all terminal — per the `prd-lifecycle-rollup` rule; product may also set it by hand. Rollup never advances a PRD to `shipped` on partial completion, and never archives a PRD page unless `notion.rollup.closeOnShipped` is configured `true` (default `false` → set `$SHIPPED`, leave the page active).
84
88
 
85
89
  ## Phases
86
90
 
@@ -99,6 +99,7 @@ Rendering rules:
99
99
  - Sort epics by key (lexical). Sort stories within an epic by key. Sort sub-tasks within a story by key. Sort the unparented list by `(type, key)`. The same sort applies to the `lisa:gw` tokens (each token sits on its entry's line), so the machine-readable order is identical across runs.
100
100
  - The line `_Generated by ..._` is fixed text — does not include a timestamp. A timestamp would defeat the diff-equality check Debrief relies on.
101
101
  - Regenerate the whole section from the current ticket set on every run — **never append**. Dedupe is by **child-ref** (the token's `ref`, per the `prd-lifecycle-rollup` idempotency dedupe key): the same ticket set produces a byte-identical section with no duplicate entries, and re-running over an existing section is a no-op diff. A ticket present in a prior run but absent now simply does not reappear (stale links never accumulate).
102
+ - **Match by stable ref, never by title** (`prd-lifecycle-rollup` idempotency dedupe key). A child is identified by its `ref` token alone — a ticket whose `title` changed but whose `ref` is unchanged is the **same** entry: its displayed title is refreshed in place and it appears exactly once, never duplicated. Title is rendered, never matched on.
102
103
 
103
104
  ## Native parent linking (GitHub)
104
105