@codyswann/lisa 2.50.0 → 2.52.0
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.
- package/package.json +1 -1
- package/plugins/lisa/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa/commands/verify-prd.md +6 -0
- package/plugins/lisa/skills/setup-confluence/SKILL.md +23 -14
- package/plugins/lisa/skills/verify-prd/SKILL.md +124 -0
- package/plugins/lisa/skills/verify-prd/agents/openai.yaml +4 -0
- package/plugins/lisa-cdk/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-cdk/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-expo/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-expo/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-harper-fabric/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-harper-fabric/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-nestjs/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-nestjs/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-openclaw/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-openclaw/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-rails/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-rails/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-typescript/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-typescript/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-wiki/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-wiki/.codex-plugin/plugin.json +1 -1
- package/plugins/src/base/commands/verify-prd.md +6 -0
- package/plugins/src/base/skills/setup-confluence/SKILL.md +23 -14
- package/plugins/src/base/skills/verify-prd/SKILL.md +124 -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.
|
|
85
|
+
"version": "2.52.0",
|
|
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": {
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: "Initiative-level PRD acceptance gate. Reads a shipped PRD and its generated child work, confirms all generated top-level work is terminal, then (sibling tickets) runs spec-conformance + empirical verification and transitions the PRD shipped → verified | blocked."
|
|
3
|
+
argument-hint: "<prd>"
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
Use the /lisa:verify-prd skill to read the PRD, confirm its generated top-level work is terminal, and run initiative-level acceptance verification. $ARGUMENTS
|
|
@@ -35,11 +35,11 @@ If neither arg is supplied, ask which mode (or both). Setting only `parentPageId
|
|
|
35
35
|
|
|
36
36
|
### Step 3 — Create lifecycle parent pages
|
|
37
37
|
|
|
38
|
-
Confluence PRD lifecycle is **parent-page-based**, not label-based (see the `config-resolution` rule for why — Atlassian's scoped API tokens cannot write labels). Each lifecycle role gets its own parent page; a PRD's state = which parent it's a child of.
|
|
38
|
+
Confluence PRD lifecycle is **parent-page-based**, not label-based (see the `config-resolution` rule for why — Atlassian's scoped API tokens cannot write labels). Each lifecycle role gets its own parent page; a PRD's state = which parent it's a child of. The full PRD lifecycle is `draft → ready → in_review → (blocked | ticketed) → shipped → verified` (the `prd-lifecycle-rollup` rule, slug `prd-lifecycle-rollup`): rollup performs the `ticketed → shipped` hop, then `/lisa:verify-prd` performs the terminal `shipped → verified` (pass) / `shipped → blocked` (fail) hop. `verified` is the terminal lifecycle state after `shipped` (the `verified` role from the `config-resolution` rule, #591).
|
|
39
39
|
|
|
40
40
|
#### 3a. Decide where the parents live
|
|
41
41
|
|
|
42
|
-
If `confluence.parentPageId` is set in config, the
|
|
42
|
+
If `confluence.parentPageId` is set in config, the seven parent pages are created as children of that page (keeps the lifecycle scoped to a sub-tree of the space). Otherwise, they're created at the space root.
|
|
43
43
|
|
|
44
44
|
```bash
|
|
45
45
|
SPACE_ID=$(curl -s -H "Authorization: Basic $AUTH" \
|
|
@@ -50,7 +50,7 @@ PARENT_ROOT=$(jq -r '.confluence.parentPageId // empty' .lisa.config.json)
|
|
|
50
50
|
|
|
51
51
|
#### 3b. Create each parent page
|
|
52
52
|
|
|
53
|
-
For each role in `[draft, ready, in_review, blocked, ticketed, shipped]`, create a page named after the role (`Draft`, `Ready`, `In Review`, `Blocked`, `Ticketed`, `Shipped`). Body: a short description of what PRDs in this state mean.
|
|
53
|
+
For each role in `[draft, ready, in_review, blocked, ticketed, shipped, verified]`, create a page named after the role (`Draft`, `Ready`, `In Review`, `Blocked`, `Ticketed`, `Shipped`, `Verified`). Body: a short description of what PRDs in this state mean.
|
|
54
54
|
|
|
55
55
|
```bash
|
|
56
56
|
create_parent() {
|
|
@@ -77,16 +77,18 @@ P_READY=$(create_parent ready "Ready" "PRDs flagged by humans as rea
|
|
|
77
77
|
P_REVIEW=$(create_parent in_review "In Review" "PRDs the agent has claimed and is validating.")
|
|
78
78
|
P_BLOCKED=$(create_parent blocked "Blocked" "Validation failed — clarifying comments posted by the agent. Edit the PRD, then move back to Ready.")
|
|
79
79
|
P_TICKETED=$(create_parent ticketed "Ticketed" "Validated and tickets created. Tracked through the build queue from here.")
|
|
80
|
-
P_SHIPPED=$(create_parent shipped "Shipped" "All child tickets shipped.
|
|
80
|
+
P_SHIPPED=$(create_parent shipped "Shipped" "All child tickets shipped.")
|
|
81
|
+
P_VERIFIED=$(create_parent verified "Verified" "Shipped product empirically checked against the PRD. Terminal state.")
|
|
81
82
|
```
|
|
82
83
|
|
|
83
|
-
Handle the "title already exists" case (400 BAD_REQUEST) by searching for an existing page with that title first and re-using its id rather than failing.
|
|
84
|
+
Handle the "title already exists" case (400 BAD_REQUEST) by searching for an existing page with that title first and re-using its id rather than failing. This find-or-reuse path is what makes the scaffolding **idempotent** — re-running `setup-confluence` reuses an existing `Verified` parent page rather than creating a duplicate.
|
|
84
85
|
|
|
85
86
|
#### 3c. Write `confluence.parents` to config
|
|
86
87
|
|
|
87
88
|
```bash
|
|
88
89
|
jq --arg d "$P_DRAFT" --arg r "$P_READY" --arg iv "$P_REVIEW" \
|
|
89
|
-
--arg b "$P_BLOCKED" --arg t "$P_TICKETED" --arg s "$P_SHIPPED"
|
|
90
|
+
--arg b "$P_BLOCKED" --arg t "$P_TICKETED" --arg s "$P_SHIPPED" \
|
|
91
|
+
--arg v "$P_VERIFIED" '
|
|
90
92
|
.confluence = ((.confluence // {})
|
|
91
93
|
| .parents = {
|
|
92
94
|
draft: $d,
|
|
@@ -94,12 +96,15 @@ jq --arg d "$P_DRAFT" --arg r "$P_READY" --arg iv "$P_REVIEW" \
|
|
|
94
96
|
in_review: $iv,
|
|
95
97
|
blocked: $b,
|
|
96
98
|
ticketed: $t,
|
|
97
|
-
shipped: $s
|
|
99
|
+
shipped: $s,
|
|
100
|
+
verified: $v
|
|
98
101
|
})
|
|
99
102
|
' .lisa.config.json > .lisa.config.json.tmp \
|
|
100
103
|
&& mv .lisa.config.json.tmp .lisa.config.json
|
|
101
104
|
```
|
|
102
105
|
|
|
106
|
+
This persists `confluence.parents.verified` to config, matching the `confluence.parents.verified` schema from the `config-resolution` rule, #591.
|
|
107
|
+
|
|
103
108
|
### Step 4 — Write top-level `confluence` section (spaceKey / parentPageId)
|
|
104
109
|
|
|
105
110
|
```bash
|
|
@@ -132,7 +137,7 @@ jq '.source = "confluence"' .lisa.config.json > .lisa.config.json.tmp \
|
|
|
132
137
|
|
|
133
138
|
### Step 6 — Create / update PRD Dashboard page
|
|
134
139
|
|
|
135
|
-
Confluence has no native swimlane / kanban view for label-driven lifecycles. To approximate the Notion-board experience, create a single "PRD Dashboard" page in the configured space that renders
|
|
140
|
+
Confluence has no native swimlane / kanban view for label-driven lifecycles. To approximate the Notion-board experience, create a single "PRD Dashboard" page in the configured space that renders seven `Children Display` macros side-by-side — one per PRD lifecycle status (`draft`, `ready`, `in_review`, `blocked`, `ticketed`, `shipped`, `verified`). The dashboard is the team's single-screen view of the PRD pipeline.
|
|
136
141
|
|
|
137
142
|
The `draft` column captures PRDs that have been created but not yet flipped to `ready` — useful for authors to track their own in-flight work and for editors to find PRDs that need a polish pass before they hit the agent queue.
|
|
138
143
|
|
|
@@ -142,7 +147,7 @@ If `confluence.dashboardPageId` already exists in `.lisa.config.json`, update th
|
|
|
142
147
|
|
|
143
148
|
#### Build the page body (Confluence storage format)
|
|
144
149
|
|
|
145
|
-
|
|
150
|
+
Seven columns inside a `ac:layout` block. Each column is a `Children Display` macro targeting one lifecycle parent, scoped to the configured space (or parent page, if set).
|
|
146
151
|
|
|
147
152
|
Read the parent page IDs from config:
|
|
148
153
|
|
|
@@ -153,6 +158,7 @@ P_REVIEW=$(jq -r '.confluence.parents.in_review' .lisa.config.json)
|
|
|
153
158
|
P_BLOCKED=$(jq -r '.confluence.parents.blocked' .lisa.config.json)
|
|
154
159
|
P_TICKETED=$(jq -r '.confluence.parents.ticketed' .lisa.config.json)
|
|
155
160
|
P_SHIPPED=$(jq -r '.confluence.parents.shipped' .lisa.config.json)
|
|
161
|
+
P_VERIFIED=$(jq -r '.confluence.parents.verified' .lisa.config.json)
|
|
156
162
|
```
|
|
157
163
|
|
|
158
164
|
Build a `Children Display` macro per parent. The macro shows direct children of the specified page, automatically updating as PRDs move between parents:
|
|
@@ -169,9 +175,9 @@ Build a `Children Display` macro per parent. The macro shows direct children of
|
|
|
169
175
|
|
|
170
176
|
Children Display targets a parent by **content-title** (not by id, in storage format). The `ri:content-title` attribute references the parent by its title within the current space.
|
|
171
177
|
|
|
172
|
-
Wrap the
|
|
178
|
+
Wrap the seven macros in three rows of `ac:layout-section`. Confluence's `ac:layout` has no native 7-column preset, so lay out as three rows: two `three_equal` rows plus a final single-column row for the seventh tile. Row 1 (`three_equal`): Draft, Ready, In Review. Row 2 (`three_equal`): Blocked, Ticketed, Shipped. Row 3 (`single`): Verified.
|
|
173
179
|
|
|
174
|
-
The grouping is semantic too — top row covers the human-driven lead-up to agent pickup;
|
|
180
|
+
The grouping is semantic too — top row covers the human-driven lead-up to agent pickup; middle row covers agent-driven build states post-pickup; the final row is the terminal `verified` state where the shipped product has been empirically checked against the PRD itself (`/lisa:verify-prd`).
|
|
175
181
|
|
|
176
182
|
Heading each column with the status name and count keeps the board scannable:
|
|
177
183
|
|
|
@@ -185,9 +191,10 @@ Heading each column with the status name and count keeps the board scannable:
|
|
|
185
191
|
Above the layout, include a short header describing what the page is and how to use it:
|
|
186
192
|
|
|
187
193
|
```xml
|
|
188
|
-
<p>This page is the PRD pipeline view. PRDs live as child pages of one of
|
|
194
|
+
<p>This page is the PRD pipeline view. PRDs live as child pages of one of seven lifecycle
|
|
189
195
|
parents: <strong>Draft</strong>, <strong>Ready</strong>, <strong>In Review</strong>,
|
|
190
|
-
<strong>Blocked</strong>, <strong>Ticketed</strong>, <strong>Shipped</strong
|
|
196
|
+
<strong>Blocked</strong>, <strong>Ticketed</strong>, <strong>Shipped</strong>,
|
|
197
|
+
<strong>Verified</strong>.
|
|
191
198
|
Move a PRD between states by re-parenting the page (drag in the page tree, or
|
|
192
199
|
via the page's location settings).</p>
|
|
193
200
|
<p>To add a new PRD: create a page under <strong>Draft</strong>. When ready for
|
|
@@ -228,14 +235,16 @@ Skip Step 6 entirely if:
|
|
|
228
235
|
|
|
229
236
|
```bash
|
|
230
237
|
jq -e '.confluence.spaceKey // .confluence.parentPageId' .lisa.config.json >/dev/null
|
|
238
|
+
jq -e '.confluence.parents.verified' .lisa.config.json >/dev/null
|
|
231
239
|
```
|
|
232
240
|
|
|
233
|
-
Report success with the resolved scope (`spaceKey`, `parentPageId`, or both), whether `source` was set, and the PRD Dashboard URL if Step 6 ran.
|
|
241
|
+
Report success with the resolved scope (`spaceKey`, `parentPageId`, or both), the seven lifecycle parents created or reused (including the terminal `verified` parent), whether `source` was set, and the PRD Dashboard URL if Step 6 ran.
|
|
234
242
|
|
|
235
243
|
## Idempotency
|
|
236
244
|
|
|
237
245
|
- Re-running replaces fields cleanly (jq merge).
|
|
238
246
|
- Re-running does not re-prompt for `source` if it's already `"confluence"`.
|
|
247
|
+
- Re-running reuses an existing lifecycle parent page (by title) rather than duplicating it — so the terminal `Verified` parent is created once and reused on every subsequent run.
|
|
239
248
|
- Re-running with an existing `dashboardPageId` updates the page in place rather than creating duplicates.
|
|
240
249
|
|
|
241
250
|
## Rules
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: verify-prd
|
|
3
|
+
description: "Initiative-level PRD acceptance gate. Given a PRD ref/URL (GitHub Issue, Linear project/issue, Notion page, Confluence page, or JIRA issue), resolves the source vendor, reads the PRD body and its generated top-level child work set via the prd-lifecycle-rollup contract (native hierarchy first, machine-readable generated-work section fallback — never reimplementing child enumeration), and confirms every required generated top-level work item is terminal before any empirical verification runs. If any required top-level child is non-terminal, it reports the incomplete child set and STOPS without verifying or transitioning the PRD. The entry point of the PRD-level verification flow; the PASS path (spec-conformance + empirical verification + shipped → verified), the FAIL path (shipped → blocked + fix issues), and idempotency are handled by sibling work."
|
|
4
|
+
allowed-tools: ["Skill", "Bash", "Read", "mcp__claude_ai_Notion__notion-fetch", "mcp__claude_ai_Notion__notion-get-comments", "mcp__atlassian__getConfluencePage", "mcp__atlassian__getConfluencePageDescendants", "mcp__atlassian__getJiraIssue", "mcp__atlassian__searchJiraIssuesUsingJql", "mcp__atlassian__getAccessibleAtlassianResources", "mcp__linear-server__get_project", "mcp__linear-server__list_issues", "mcp__linear-server__get_issue", "mcp__linear-server__list_documents", "mcp__linear-server__get_document"]
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# PRD-level Verification: $ARGUMENTS
|
|
8
|
+
|
|
9
|
+
`/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.
|
|
10
|
+
|
|
11
|
+
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.
|
|
12
|
+
|
|
13
|
+
`$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**.
|
|
14
|
+
|
|
15
|
+
## Confirmation policy
|
|
16
|
+
|
|
17
|
+
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.
|
|
18
|
+
|
|
19
|
+
## Scope of this skill
|
|
20
|
+
|
|
21
|
+
This skill is the **read/guard front-half** of PRD-level verification:
|
|
22
|
+
|
|
23
|
+
1. Resolve the PRD ref and detect its source vendor.
|
|
24
|
+
2. Read the PRD body and its **generated top-level child work set** via the `prd-lifecycle-rollup` contract.
|
|
25
|
+
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 empirical verification, do not transition the PRD.
|
|
26
|
+
|
|
27
|
+
The remaining phases of PRD-level verification are **out of scope** here and are delivered by sibling work (this skill is their entry point):
|
|
28
|
+
|
|
29
|
+
- **PASS path** — spec-conformance against the PRD's requirements + empirical verification of the shipped surface, then the `shipped → verified` transition with evidence.
|
|
30
|
+
- **FAIL path** — on verification failure, the `shipped → blocked` transition, a product-readable failure report, and linked fix issues for the missing/incorrect behavior.
|
|
31
|
+
- **Idempotency** — re-runs producing no duplicate evidence, fix issues, or lifecycle transitions.
|
|
32
|
+
|
|
33
|
+
When the terminal-child guard passes (all required generated top-level work is terminal), this front-half hands off to the PASS/FAIL phases. Until those phases land, a passing guard means "ready for verification"; this skill does not itself transition the PRD to `verified` or `blocked`.
|
|
34
|
+
|
|
35
|
+
## Phase 1 — Resolve the PRD ref and detect the source vendor
|
|
36
|
+
|
|
37
|
+
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:
|
|
38
|
+
|
|
39
|
+
| Input shape | Vendor | Read surface |
|
|
40
|
+
|---|---|---|
|
|
41
|
+
| `github.com/<org>/<repo>/issues/<n>` or `<org>/<repo>#<n>` | **GitHub Issues** | `gh` CLI (Lisa uses the CLI exclusively for GitHub — no GitHub MCP) |
|
|
42
|
+
| `linear.app/...` | **Linear** | `mcp__linear-server__get_project` / `get_issue` |
|
|
43
|
+
| `notion.so` / `notion.site` | **Notion** | `mcp__claude_ai_Notion__notion-fetch` (`include_discussions: true`) |
|
|
44
|
+
| `*.atlassian.net/wiki/...` | **Confluence** | `mcp__atlassian__getConfluencePage` (+ descendants) |
|
|
45
|
+
| JIRA issue key (e.g. `PROJ-123`) or `*.atlassian.net/browse/...` | **JIRA** | `mcp__atlassian__getJiraIssue` |
|
|
46
|
+
|
|
47
|
+
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).
|
|
48
|
+
|
|
49
|
+
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.
|
|
50
|
+
|
|
51
|
+
## Phase 2 — Read the generated top-level child work set
|
|
52
|
+
|
|
53
|
+
**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.
|
|
54
|
+
|
|
55
|
+
Use two sources, **native first**, deduped by child-ref identity:
|
|
56
|
+
|
|
57
|
+
1. **Native hierarchy (primary).** Read the PRD's direct native children — these are its top-level children:
|
|
58
|
+
- **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`).
|
|
59
|
+
- **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).
|
|
60
|
+
- **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`.
|
|
61
|
+
- **Notion / Confluence** — no native issue hierarchy; use the documented section below.
|
|
62
|
+
|
|
63
|
+
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.
|
|
64
|
+
|
|
65
|
+
**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.**
|
|
66
|
+
|
|
67
|
+
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.
|
|
68
|
+
|
|
69
|
+
## Phase 3 — Terminal-child guard
|
|
70
|
+
|
|
71
|
+
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:
|
|
72
|
+
|
|
73
|
+
- **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.
|
|
74
|
+
- **Terminal-but-dropped** — closed-as-not-planned (GitHub `stateReason == "not_planned"`) / canceled (Linear) / won't-do. It does **not** hold the PRD open and is excluded from the required set.
|
|
75
|
+
- **Incomplete / blocked** — anything else (still open, or closed without the `done` role). It holds the PRD open.
|
|
76
|
+
|
|
77
|
+
The **required** set is the top-level children minus the terminal-but-dropped ones. Branch:
|
|
78
|
+
|
|
79
|
+
**Any required top-level child is non-terminal** (the "Given a PRD has generated child work that is not terminal" scenario):
|
|
80
|
+
|
|
81
|
+
1. **STOP.** Do **not** run empirical verification or spec-conformance.
|
|
82
|
+
2. **Leave the PRD lifecycle untouched** — it stays at `shipped`. Do not transition it to `verified` or `blocked`; do not close or archive it.
|
|
83
|
+
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.
|
|
84
|
+
|
|
85
|
+
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.
|
|
86
|
+
|
|
87
|
+
**All required top-level children are terminal** (at least one required child exists): the guard passes. Hand off to the PASS/FAIL verification phases (sibling work, out of scope here — see [Scope of this skill](#scope-of-this-skill)). Until those phases land, report `terminal-child guard PASSED — <n> required top-level children terminal; ready for PRD-level verification` and stop.
|
|
88
|
+
|
|
89
|
+
## Output
|
|
90
|
+
|
|
91
|
+
Emit a single fenced text block so callers can parse it.
|
|
92
|
+
|
|
93
|
+
```text
|
|
94
|
+
## verify-prd: <PRD title>
|
|
95
|
+
|
|
96
|
+
PRD: <ref/URL> (vendor: <github|linear|notion|confluence|jira>)
|
|
97
|
+
PRD lifecycle state: shipped
|
|
98
|
+
Generated top-level children read: <n> (source: native | documented | both)
|
|
99
|
+
|
|
100
|
+
### Terminal-child guard
|
|
101
|
+
- <ref> "<title>" — <terminal|terminal-but-dropped|incomplete>: <state>
|
|
102
|
+
- ...
|
|
103
|
+
|
|
104
|
+
Required top-level children: <n> Terminal: <n> Incomplete: <n>
|
|
105
|
+
|
|
106
|
+
### Verdict: GUARD_PASSED | GUARD_BLOCKED | NO_CHILDREN
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
- `GUARD_BLOCKED` — one or more required top-level children are non-terminal; verification did not run; the PRD was left at `shipped`.
|
|
110
|
+
- `GUARD_PASSED` — all required top-level children are terminal; ready to hand off to PRD-level verification (PASS/FAIL phases — sibling work).
|
|
111
|
+
- `NO_CHILDREN` — no generated top-level children found; cannot verify; the PRD was left untouched.
|
|
112
|
+
|
|
113
|
+
## Rules
|
|
114
|
+
|
|
115
|
+
- **Read-only in this front-half.** This skill resolves the PRD, reads the child set, and applies the guard. It never transitions the PRD lifecycle — neither the guard-blocked path (leave at `shipped`) nor the guard-passed path (hand off; the `shipped → verified | blocked` hops are owned by the PASS/FAIL sibling work, not by this scaffold).
|
|
116
|
+
- **Never reimplement child enumeration.** Consume the recorded PRD→child relationship (`prd-lifecycle-rollup` native linking + machine-readable generated-work section). The two-source read here mirrors `github-prd-intake` Phase 3f.2 — same sources, same dedupe-by-child-ref, same top-level-only boundary.
|
|
117
|
+
- **Top-level only.** Exclude leaf Sub-tasks and Stories nested under a generated Epic. The PRD owns its top-level work; those top-level units own their descendants (`prd-lifecycle-rollup` generated-top-level-work contract).
|
|
118
|
+
- **Cite, don't restate.** The generated-top-level-work boundary, the per-vendor terminal predicate, the env-keyed `done` resolution, and the dedupe-by-child-ref idempotency key all come from the `prd-lifecycle-rollup` rule. This skill is a consumer of that contract, not a second source of truth.
|
|
119
|
+
|
|
120
|
+
## Related rules
|
|
121
|
+
|
|
122
|
+
- `prd-lifecycle-rollup` — the vendor-neutral source of truth for PRD→generated-top-level-work ownership, the per-vendor terminal predicate, the `shipped` rollup, the `shipped → verified | blocked` PRD-level verification hops, and the child-ref idempotency dedupe key. This skill consumes that contract; it cites the rule by slug rather than restating its taxonomy.
|
|
123
|
+
- `leaf-only-lifecycle` — governs the build lifecycle of leaf work units and how a generated Epic rolls up from its own children; this skill trusts that bottom-up rollup when reading a top-level child's resolved state.
|
|
124
|
+
- `config-resolution` — the PRD-lifecycle role vocabulary (`shipped`, `verified`, `blocked`) and the env-keyed `done` map the terminal predicate resolves against.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "lisa-openclaw",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.52.0",
|
|
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.
|
|
3
|
+
"version": "2.52.0",
|
|
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"
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: "Initiative-level PRD acceptance gate. Reads a shipped PRD and its generated child work, confirms all generated top-level work is terminal, then (sibling tickets) runs spec-conformance + empirical verification and transitions the PRD shipped → verified | blocked."
|
|
3
|
+
argument-hint: "<prd>"
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
Use the /lisa:verify-prd skill to read the PRD, confirm its generated top-level work is terminal, and run initiative-level acceptance verification. $ARGUMENTS
|
|
@@ -35,11 +35,11 @@ If neither arg is supplied, ask which mode (or both). Setting only `parentPageId
|
|
|
35
35
|
|
|
36
36
|
### Step 3 — Create lifecycle parent pages
|
|
37
37
|
|
|
38
|
-
Confluence PRD lifecycle is **parent-page-based**, not label-based (see the `config-resolution` rule for why — Atlassian's scoped API tokens cannot write labels). Each lifecycle role gets its own parent page; a PRD's state = which parent it's a child of.
|
|
38
|
+
Confluence PRD lifecycle is **parent-page-based**, not label-based (see the `config-resolution` rule for why — Atlassian's scoped API tokens cannot write labels). Each lifecycle role gets its own parent page; a PRD's state = which parent it's a child of. The full PRD lifecycle is `draft → ready → in_review → (blocked | ticketed) → shipped → verified` (the `prd-lifecycle-rollup` rule, slug `prd-lifecycle-rollup`): rollup performs the `ticketed → shipped` hop, then `/lisa:verify-prd` performs the terminal `shipped → verified` (pass) / `shipped → blocked` (fail) hop. `verified` is the terminal lifecycle state after `shipped` (the `verified` role from the `config-resolution` rule, #591).
|
|
39
39
|
|
|
40
40
|
#### 3a. Decide where the parents live
|
|
41
41
|
|
|
42
|
-
If `confluence.parentPageId` is set in config, the
|
|
42
|
+
If `confluence.parentPageId` is set in config, the seven parent pages are created as children of that page (keeps the lifecycle scoped to a sub-tree of the space). Otherwise, they're created at the space root.
|
|
43
43
|
|
|
44
44
|
```bash
|
|
45
45
|
SPACE_ID=$(curl -s -H "Authorization: Basic $AUTH" \
|
|
@@ -50,7 +50,7 @@ PARENT_ROOT=$(jq -r '.confluence.parentPageId // empty' .lisa.config.json)
|
|
|
50
50
|
|
|
51
51
|
#### 3b. Create each parent page
|
|
52
52
|
|
|
53
|
-
For each role in `[draft, ready, in_review, blocked, ticketed, shipped]`, create a page named after the role (`Draft`, `Ready`, `In Review`, `Blocked`, `Ticketed`, `Shipped`). Body: a short description of what PRDs in this state mean.
|
|
53
|
+
For each role in `[draft, ready, in_review, blocked, ticketed, shipped, verified]`, create a page named after the role (`Draft`, `Ready`, `In Review`, `Blocked`, `Ticketed`, `Shipped`, `Verified`). Body: a short description of what PRDs in this state mean.
|
|
54
54
|
|
|
55
55
|
```bash
|
|
56
56
|
create_parent() {
|
|
@@ -77,16 +77,18 @@ P_READY=$(create_parent ready "Ready" "PRDs flagged by humans as rea
|
|
|
77
77
|
P_REVIEW=$(create_parent in_review "In Review" "PRDs the agent has claimed and is validating.")
|
|
78
78
|
P_BLOCKED=$(create_parent blocked "Blocked" "Validation failed — clarifying comments posted by the agent. Edit the PRD, then move back to Ready.")
|
|
79
79
|
P_TICKETED=$(create_parent ticketed "Ticketed" "Validated and tickets created. Tracked through the build queue from here.")
|
|
80
|
-
P_SHIPPED=$(create_parent shipped "Shipped" "All child tickets shipped.
|
|
80
|
+
P_SHIPPED=$(create_parent shipped "Shipped" "All child tickets shipped.")
|
|
81
|
+
P_VERIFIED=$(create_parent verified "Verified" "Shipped product empirically checked against the PRD. Terminal state.")
|
|
81
82
|
```
|
|
82
83
|
|
|
83
|
-
Handle the "title already exists" case (400 BAD_REQUEST) by searching for an existing page with that title first and re-using its id rather than failing.
|
|
84
|
+
Handle the "title already exists" case (400 BAD_REQUEST) by searching for an existing page with that title first and re-using its id rather than failing. This find-or-reuse path is what makes the scaffolding **idempotent** — re-running `setup-confluence` reuses an existing `Verified` parent page rather than creating a duplicate.
|
|
84
85
|
|
|
85
86
|
#### 3c. Write `confluence.parents` to config
|
|
86
87
|
|
|
87
88
|
```bash
|
|
88
89
|
jq --arg d "$P_DRAFT" --arg r "$P_READY" --arg iv "$P_REVIEW" \
|
|
89
|
-
--arg b "$P_BLOCKED" --arg t "$P_TICKETED" --arg s "$P_SHIPPED"
|
|
90
|
+
--arg b "$P_BLOCKED" --arg t "$P_TICKETED" --arg s "$P_SHIPPED" \
|
|
91
|
+
--arg v "$P_VERIFIED" '
|
|
90
92
|
.confluence = ((.confluence // {})
|
|
91
93
|
| .parents = {
|
|
92
94
|
draft: $d,
|
|
@@ -94,12 +96,15 @@ jq --arg d "$P_DRAFT" --arg r "$P_READY" --arg iv "$P_REVIEW" \
|
|
|
94
96
|
in_review: $iv,
|
|
95
97
|
blocked: $b,
|
|
96
98
|
ticketed: $t,
|
|
97
|
-
shipped: $s
|
|
99
|
+
shipped: $s,
|
|
100
|
+
verified: $v
|
|
98
101
|
})
|
|
99
102
|
' .lisa.config.json > .lisa.config.json.tmp \
|
|
100
103
|
&& mv .lisa.config.json.tmp .lisa.config.json
|
|
101
104
|
```
|
|
102
105
|
|
|
106
|
+
This persists `confluence.parents.verified` to config, matching the `confluence.parents.verified` schema from the `config-resolution` rule, #591.
|
|
107
|
+
|
|
103
108
|
### Step 4 — Write top-level `confluence` section (spaceKey / parentPageId)
|
|
104
109
|
|
|
105
110
|
```bash
|
|
@@ -132,7 +137,7 @@ jq '.source = "confluence"' .lisa.config.json > .lisa.config.json.tmp \
|
|
|
132
137
|
|
|
133
138
|
### Step 6 — Create / update PRD Dashboard page
|
|
134
139
|
|
|
135
|
-
Confluence has no native swimlane / kanban view for label-driven lifecycles. To approximate the Notion-board experience, create a single "PRD Dashboard" page in the configured space that renders
|
|
140
|
+
Confluence has no native swimlane / kanban view for label-driven lifecycles. To approximate the Notion-board experience, create a single "PRD Dashboard" page in the configured space that renders seven `Children Display` macros side-by-side — one per PRD lifecycle status (`draft`, `ready`, `in_review`, `blocked`, `ticketed`, `shipped`, `verified`). The dashboard is the team's single-screen view of the PRD pipeline.
|
|
136
141
|
|
|
137
142
|
The `draft` column captures PRDs that have been created but not yet flipped to `ready` — useful for authors to track their own in-flight work and for editors to find PRDs that need a polish pass before they hit the agent queue.
|
|
138
143
|
|
|
@@ -142,7 +147,7 @@ If `confluence.dashboardPageId` already exists in `.lisa.config.json`, update th
|
|
|
142
147
|
|
|
143
148
|
#### Build the page body (Confluence storage format)
|
|
144
149
|
|
|
145
|
-
|
|
150
|
+
Seven columns inside a `ac:layout` block. Each column is a `Children Display` macro targeting one lifecycle parent, scoped to the configured space (or parent page, if set).
|
|
146
151
|
|
|
147
152
|
Read the parent page IDs from config:
|
|
148
153
|
|
|
@@ -153,6 +158,7 @@ P_REVIEW=$(jq -r '.confluence.parents.in_review' .lisa.config.json)
|
|
|
153
158
|
P_BLOCKED=$(jq -r '.confluence.parents.blocked' .lisa.config.json)
|
|
154
159
|
P_TICKETED=$(jq -r '.confluence.parents.ticketed' .lisa.config.json)
|
|
155
160
|
P_SHIPPED=$(jq -r '.confluence.parents.shipped' .lisa.config.json)
|
|
161
|
+
P_VERIFIED=$(jq -r '.confluence.parents.verified' .lisa.config.json)
|
|
156
162
|
```
|
|
157
163
|
|
|
158
164
|
Build a `Children Display` macro per parent. The macro shows direct children of the specified page, automatically updating as PRDs move between parents:
|
|
@@ -169,9 +175,9 @@ Build a `Children Display` macro per parent. The macro shows direct children of
|
|
|
169
175
|
|
|
170
176
|
Children Display targets a parent by **content-title** (not by id, in storage format). The `ri:content-title` attribute references the parent by its title within the current space.
|
|
171
177
|
|
|
172
|
-
Wrap the
|
|
178
|
+
Wrap the seven macros in three rows of `ac:layout-section`. Confluence's `ac:layout` has no native 7-column preset, so lay out as three rows: two `three_equal` rows plus a final single-column row for the seventh tile. Row 1 (`three_equal`): Draft, Ready, In Review. Row 2 (`three_equal`): Blocked, Ticketed, Shipped. Row 3 (`single`): Verified.
|
|
173
179
|
|
|
174
|
-
The grouping is semantic too — top row covers the human-driven lead-up to agent pickup;
|
|
180
|
+
The grouping is semantic too — top row covers the human-driven lead-up to agent pickup; middle row covers agent-driven build states post-pickup; the final row is the terminal `verified` state where the shipped product has been empirically checked against the PRD itself (`/lisa:verify-prd`).
|
|
175
181
|
|
|
176
182
|
Heading each column with the status name and count keeps the board scannable:
|
|
177
183
|
|
|
@@ -185,9 +191,10 @@ Heading each column with the status name and count keeps the board scannable:
|
|
|
185
191
|
Above the layout, include a short header describing what the page is and how to use it:
|
|
186
192
|
|
|
187
193
|
```xml
|
|
188
|
-
<p>This page is the PRD pipeline view. PRDs live as child pages of one of
|
|
194
|
+
<p>This page is the PRD pipeline view. PRDs live as child pages of one of seven lifecycle
|
|
189
195
|
parents: <strong>Draft</strong>, <strong>Ready</strong>, <strong>In Review</strong>,
|
|
190
|
-
<strong>Blocked</strong>, <strong>Ticketed</strong>, <strong>Shipped</strong
|
|
196
|
+
<strong>Blocked</strong>, <strong>Ticketed</strong>, <strong>Shipped</strong>,
|
|
197
|
+
<strong>Verified</strong>.
|
|
191
198
|
Move a PRD between states by re-parenting the page (drag in the page tree, or
|
|
192
199
|
via the page's location settings).</p>
|
|
193
200
|
<p>To add a new PRD: create a page under <strong>Draft</strong>. When ready for
|
|
@@ -228,14 +235,16 @@ Skip Step 6 entirely if:
|
|
|
228
235
|
|
|
229
236
|
```bash
|
|
230
237
|
jq -e '.confluence.spaceKey // .confluence.parentPageId' .lisa.config.json >/dev/null
|
|
238
|
+
jq -e '.confluence.parents.verified' .lisa.config.json >/dev/null
|
|
231
239
|
```
|
|
232
240
|
|
|
233
|
-
Report success with the resolved scope (`spaceKey`, `parentPageId`, or both), whether `source` was set, and the PRD Dashboard URL if Step 6 ran.
|
|
241
|
+
Report success with the resolved scope (`spaceKey`, `parentPageId`, or both), the seven lifecycle parents created or reused (including the terminal `verified` parent), whether `source` was set, and the PRD Dashboard URL if Step 6 ran.
|
|
234
242
|
|
|
235
243
|
## Idempotency
|
|
236
244
|
|
|
237
245
|
- Re-running replaces fields cleanly (jq merge).
|
|
238
246
|
- Re-running does not re-prompt for `source` if it's already `"confluence"`.
|
|
247
|
+
- Re-running reuses an existing lifecycle parent page (by title) rather than duplicating it — so the terminal `Verified` parent is created once and reused on every subsequent run.
|
|
239
248
|
- Re-running with an existing `dashboardPageId` updates the page in place rather than creating duplicates.
|
|
240
249
|
|
|
241
250
|
## Rules
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: verify-prd
|
|
3
|
+
description: "Initiative-level PRD acceptance gate. Given a PRD ref/URL (GitHub Issue, Linear project/issue, Notion page, Confluence page, or JIRA issue), resolves the source vendor, reads the PRD body and its generated top-level child work set via the prd-lifecycle-rollup contract (native hierarchy first, machine-readable generated-work section fallback — never reimplementing child enumeration), and confirms every required generated top-level work item is terminal before any empirical verification runs. If any required top-level child is non-terminal, it reports the incomplete child set and STOPS without verifying or transitioning the PRD. The entry point of the PRD-level verification flow; the PASS path (spec-conformance + empirical verification + shipped → verified), the FAIL path (shipped → blocked + fix issues), and idempotency are handled by sibling work."
|
|
4
|
+
allowed-tools: ["Skill", "Bash", "Read", "mcp__claude_ai_Notion__notion-fetch", "mcp__claude_ai_Notion__notion-get-comments", "mcp__atlassian__getConfluencePage", "mcp__atlassian__getConfluencePageDescendants", "mcp__atlassian__getJiraIssue", "mcp__atlassian__searchJiraIssuesUsingJql", "mcp__atlassian__getAccessibleAtlassianResources", "mcp__linear-server__get_project", "mcp__linear-server__list_issues", "mcp__linear-server__get_issue", "mcp__linear-server__list_documents", "mcp__linear-server__get_document"]
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# PRD-level Verification: $ARGUMENTS
|
|
8
|
+
|
|
9
|
+
`/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.
|
|
10
|
+
|
|
11
|
+
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.
|
|
12
|
+
|
|
13
|
+
`$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**.
|
|
14
|
+
|
|
15
|
+
## Confirmation policy
|
|
16
|
+
|
|
17
|
+
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.
|
|
18
|
+
|
|
19
|
+
## Scope of this skill
|
|
20
|
+
|
|
21
|
+
This skill is the **read/guard front-half** of PRD-level verification:
|
|
22
|
+
|
|
23
|
+
1. Resolve the PRD ref and detect its source vendor.
|
|
24
|
+
2. Read the PRD body and its **generated top-level child work set** via the `prd-lifecycle-rollup` contract.
|
|
25
|
+
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 empirical verification, do not transition the PRD.
|
|
26
|
+
|
|
27
|
+
The remaining phases of PRD-level verification are **out of scope** here and are delivered by sibling work (this skill is their entry point):
|
|
28
|
+
|
|
29
|
+
- **PASS path** — spec-conformance against the PRD's requirements + empirical verification of the shipped surface, then the `shipped → verified` transition with evidence.
|
|
30
|
+
- **FAIL path** — on verification failure, the `shipped → blocked` transition, a product-readable failure report, and linked fix issues for the missing/incorrect behavior.
|
|
31
|
+
- **Idempotency** — re-runs producing no duplicate evidence, fix issues, or lifecycle transitions.
|
|
32
|
+
|
|
33
|
+
When the terminal-child guard passes (all required generated top-level work is terminal), this front-half hands off to the PASS/FAIL phases. Until those phases land, a passing guard means "ready for verification"; this skill does not itself transition the PRD to `verified` or `blocked`.
|
|
34
|
+
|
|
35
|
+
## Phase 1 — Resolve the PRD ref and detect the source vendor
|
|
36
|
+
|
|
37
|
+
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:
|
|
38
|
+
|
|
39
|
+
| Input shape | Vendor | Read surface |
|
|
40
|
+
|---|---|---|
|
|
41
|
+
| `github.com/<org>/<repo>/issues/<n>` or `<org>/<repo>#<n>` | **GitHub Issues** | `gh` CLI (Lisa uses the CLI exclusively for GitHub — no GitHub MCP) |
|
|
42
|
+
| `linear.app/...` | **Linear** | `mcp__linear-server__get_project` / `get_issue` |
|
|
43
|
+
| `notion.so` / `notion.site` | **Notion** | `mcp__claude_ai_Notion__notion-fetch` (`include_discussions: true`) |
|
|
44
|
+
| `*.atlassian.net/wiki/...` | **Confluence** | `mcp__atlassian__getConfluencePage` (+ descendants) |
|
|
45
|
+
| JIRA issue key (e.g. `PROJ-123`) or `*.atlassian.net/browse/...` | **JIRA** | `mcp__atlassian__getJiraIssue` |
|
|
46
|
+
|
|
47
|
+
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).
|
|
48
|
+
|
|
49
|
+
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.
|
|
50
|
+
|
|
51
|
+
## Phase 2 — Read the generated top-level child work set
|
|
52
|
+
|
|
53
|
+
**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.
|
|
54
|
+
|
|
55
|
+
Use two sources, **native first**, deduped by child-ref identity:
|
|
56
|
+
|
|
57
|
+
1. **Native hierarchy (primary).** Read the PRD's direct native children — these are its top-level children:
|
|
58
|
+
- **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`).
|
|
59
|
+
- **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).
|
|
60
|
+
- **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`.
|
|
61
|
+
- **Notion / Confluence** — no native issue hierarchy; use the documented section below.
|
|
62
|
+
|
|
63
|
+
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.
|
|
64
|
+
|
|
65
|
+
**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.**
|
|
66
|
+
|
|
67
|
+
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.
|
|
68
|
+
|
|
69
|
+
## Phase 3 — Terminal-child guard
|
|
70
|
+
|
|
71
|
+
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:
|
|
72
|
+
|
|
73
|
+
- **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.
|
|
74
|
+
- **Terminal-but-dropped** — closed-as-not-planned (GitHub `stateReason == "not_planned"`) / canceled (Linear) / won't-do. It does **not** hold the PRD open and is excluded from the required set.
|
|
75
|
+
- **Incomplete / blocked** — anything else (still open, or closed without the `done` role). It holds the PRD open.
|
|
76
|
+
|
|
77
|
+
The **required** set is the top-level children minus the terminal-but-dropped ones. Branch:
|
|
78
|
+
|
|
79
|
+
**Any required top-level child is non-terminal** (the "Given a PRD has generated child work that is not terminal" scenario):
|
|
80
|
+
|
|
81
|
+
1. **STOP.** Do **not** run empirical verification or spec-conformance.
|
|
82
|
+
2. **Leave the PRD lifecycle untouched** — it stays at `shipped`. Do not transition it to `verified` or `blocked`; do not close or archive it.
|
|
83
|
+
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.
|
|
84
|
+
|
|
85
|
+
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.
|
|
86
|
+
|
|
87
|
+
**All required top-level children are terminal** (at least one required child exists): the guard passes. Hand off to the PASS/FAIL verification phases (sibling work, out of scope here — see [Scope of this skill](#scope-of-this-skill)). Until those phases land, report `terminal-child guard PASSED — <n> required top-level children terminal; ready for PRD-level verification` and stop.
|
|
88
|
+
|
|
89
|
+
## Output
|
|
90
|
+
|
|
91
|
+
Emit a single fenced text block so callers can parse it.
|
|
92
|
+
|
|
93
|
+
```text
|
|
94
|
+
## verify-prd: <PRD title>
|
|
95
|
+
|
|
96
|
+
PRD: <ref/URL> (vendor: <github|linear|notion|confluence|jira>)
|
|
97
|
+
PRD lifecycle state: shipped
|
|
98
|
+
Generated top-level children read: <n> (source: native | documented | both)
|
|
99
|
+
|
|
100
|
+
### Terminal-child guard
|
|
101
|
+
- <ref> "<title>" — <terminal|terminal-but-dropped|incomplete>: <state>
|
|
102
|
+
- ...
|
|
103
|
+
|
|
104
|
+
Required top-level children: <n> Terminal: <n> Incomplete: <n>
|
|
105
|
+
|
|
106
|
+
### Verdict: GUARD_PASSED | GUARD_BLOCKED | NO_CHILDREN
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
- `GUARD_BLOCKED` — one or more required top-level children are non-terminal; verification did not run; the PRD was left at `shipped`.
|
|
110
|
+
- `GUARD_PASSED` — all required top-level children are terminal; ready to hand off to PRD-level verification (PASS/FAIL phases — sibling work).
|
|
111
|
+
- `NO_CHILDREN` — no generated top-level children found; cannot verify; the PRD was left untouched.
|
|
112
|
+
|
|
113
|
+
## Rules
|
|
114
|
+
|
|
115
|
+
- **Read-only in this front-half.** This skill resolves the PRD, reads the child set, and applies the guard. It never transitions the PRD lifecycle — neither the guard-blocked path (leave at `shipped`) nor the guard-passed path (hand off; the `shipped → verified | blocked` hops are owned by the PASS/FAIL sibling work, not by this scaffold).
|
|
116
|
+
- **Never reimplement child enumeration.** Consume the recorded PRD→child relationship (`prd-lifecycle-rollup` native linking + machine-readable generated-work section). The two-source read here mirrors `github-prd-intake` Phase 3f.2 — same sources, same dedupe-by-child-ref, same top-level-only boundary.
|
|
117
|
+
- **Top-level only.** Exclude leaf Sub-tasks and Stories nested under a generated Epic. The PRD owns its top-level work; those top-level units own their descendants (`prd-lifecycle-rollup` generated-top-level-work contract).
|
|
118
|
+
- **Cite, don't restate.** The generated-top-level-work boundary, the per-vendor terminal predicate, the env-keyed `done` resolution, and the dedupe-by-child-ref idempotency key all come from the `prd-lifecycle-rollup` rule. This skill is a consumer of that contract, not a second source of truth.
|
|
119
|
+
|
|
120
|
+
## Related rules
|
|
121
|
+
|
|
122
|
+
- `prd-lifecycle-rollup` — the vendor-neutral source of truth for PRD→generated-top-level-work ownership, the per-vendor terminal predicate, the `shipped` rollup, the `shipped → verified | blocked` PRD-level verification hops, and the child-ref idempotency dedupe key. This skill consumes that contract; it cites the rule by slug rather than restating its taxonomy.
|
|
123
|
+
- `leaf-only-lifecycle` — governs the build lifecycle of leaf work units and how a generated Epic rolls up from its own children; this skill trusts that bottom-up rollup when reading a top-level child's resolved state.
|
|
124
|
+
- `config-resolution` — the PRD-lifecycle role vocabulary (`shipped`, `verified`, `blocked`) and the env-keyed `done` map the terminal predicate resolves against.
|