@codyswann/lisa 2.50.0 → 2.51.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 CHANGED
@@ -82,7 +82,7 @@
82
82
  "lodash": ">=4.18.1"
83
83
  },
84
84
  "name": "@codyswann/lisa",
85
- "version": "2.50.0",
85
+ "version": "2.51.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": {
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa",
3
- "version": "2.50.0",
3
+ "version": "2.51.0",
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.50.0",
3
+ "version": "2.51.0",
4
4
  "description": "Universal governance: agents, skills, commands, hooks, and rules for all projects.",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -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 six 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.
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. Terminal state.")
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 six `Content by Label` macros side-by-side — one per PRD lifecycle status (`draft`, `ready`, `in_review`, `blocked`, `ticketed`, `shipped`). The dashboard is the team's single-screen view of the PRD pipeline.
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
- Five columns inside a `ac:layout` block. Each column is a `Content by Label` macro filtered to one label, scoped to the configured space (or parent page, if set).
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 six macros in two rows of three columns (`ac:layout-section ac:type="three_equal"`). Confluence's `ac:layout` has no native 6-column preset; two rows of three is the cleanest readable layout. Row 1: Draft, Ready, In Review. Row 2: Blocked, Ticketed, Shipped.
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; bottom row covers agent-driven states post-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 six lifecycle
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-cdk",
3
- "version": "2.50.0",
3
+ "version": "2.51.0",
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.50.0",
3
+ "version": "2.51.0",
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.50.0",
3
+ "version": "2.51.0",
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.50.0",
3
+ "version": "2.51.0",
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.50.0",
3
+ "version": "2.51.0",
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.50.0",
3
+ "version": "2.51.0",
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.50.0",
3
+ "version": "2.51.0",
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.50.0",
3
+ "version": "2.51.0",
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.50.0",
3
+ "version": "2.51.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.50.0",
3
+ "version": "2.51.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"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-rails",
3
- "version": "2.50.0",
3
+ "version": "2.51.0",
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.50.0",
3
+ "version": "2.51.0",
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.50.0",
3
+ "version": "2.51.0",
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.50.0",
3
+ "version": "2.51.0",
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.50.0",
3
+ "version": "2.51.0",
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.50.0",
3
+ "version": "2.51.0",
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"
@@ -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 six 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.
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. Terminal state.")
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 six `Content by Label` macros side-by-side — one per PRD lifecycle status (`draft`, `ready`, `in_review`, `blocked`, `ticketed`, `shipped`). The dashboard is the team's single-screen view of the PRD pipeline.
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
- Five columns inside a `ac:layout` block. Each column is a `Content by Label` macro filtered to one label, scoped to the configured space (or parent page, if set).
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 six macros in two rows of three columns (`ac:layout-section ac:type="three_equal"`). Confluence's `ac:layout` has no native 6-column preset; two rows of three is the cleanest readable layout. Row 1: Draft, Ready, In Review. Row 2: Blocked, Ticketed, Shipped.
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; bottom row covers agent-driven states post-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 six lifecycle
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