@codyswann/lisa 2.61.0 → 2.62.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.
Files changed (150) 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 +1 -1
  5. package/plugins/lisa/agents/github-agent.md +4 -5
  6. package/plugins/lisa/agents/github-build-intake.md +1 -1
  7. package/plugins/lisa/agents/github-prd-intake.md +1 -1
  8. package/plugins/lisa/agents/linear-prd-intake.md +1 -1
  9. package/plugins/lisa/agents/notion-prd-intake.md +1 -1
  10. package/plugins/lisa/commands/intake.md +1 -1
  11. package/plugins/lisa/commands/project-ideation.md +3 -3
  12. package/plugins/lisa/commands/repair-intake.md +6 -0
  13. package/plugins/lisa/commands/research.md +3 -3
  14. package/plugins/lisa/commands/setup-automations.md +6 -0
  15. package/plugins/lisa/commands/tear-down-automations.md +6 -0
  16. package/plugins/lisa/commands/verify-prd.md +2 -2
  17. package/plugins/lisa/rules/config-resolution.md +63 -4
  18. package/plugins/lisa/rules/intent-routing.md +4 -3
  19. package/plugins/lisa/rules/leaf-only-lifecycle.md +1 -1
  20. package/plugins/lisa/rules/prd-lifecycle-rollup.md +10 -2
  21. package/plugins/lisa/rules/repo-scope-split.md +18 -1
  22. package/plugins/lisa/skills/confluence-prd-intake/SKILL.md +24 -14
  23. package/plugins/lisa/skills/confluence-prd-intake/agents/openai.yaml +2 -2
  24. package/plugins/lisa/skills/confluence-write-prd/SKILL.md +103 -0
  25. package/plugins/lisa/skills/confluence-write-prd/agents/openai.yaml +4 -0
  26. package/plugins/lisa/skills/github-build-intake/SKILL.md +32 -21
  27. package/plugins/lisa/skills/github-evidence/SKILL.md +3 -26
  28. package/plugins/lisa/skills/github-evidence/agents/openai.yaml +2 -2
  29. package/plugins/lisa/skills/github-journey/SKILL.md +2 -2
  30. package/plugins/lisa/skills/github-prd-intake/SKILL.md +25 -11
  31. package/plugins/lisa/skills/github-prd-intake/agents/openai.yaml +2 -2
  32. package/plugins/lisa/skills/github-sync/SKILL.md +5 -5
  33. package/plugins/lisa/skills/github-write-issue/SKILL.md +15 -6
  34. package/plugins/lisa/skills/github-write-prd/SKILL.md +100 -0
  35. package/plugins/lisa/skills/github-write-prd/agents/openai.yaml +4 -0
  36. package/plugins/lisa/skills/implement/SKILL.md +13 -6
  37. package/plugins/lisa/skills/intake/SKILL.md +13 -12
  38. package/plugins/lisa/skills/intake/agents/openai.yaml +2 -2
  39. package/plugins/lisa/skills/jira-build-intake/SKILL.md +24 -11
  40. package/plugins/lisa/skills/jira-write-ticket/SKILL.md +8 -0
  41. package/plugins/lisa/skills/linear-build-intake/SKILL.md +22 -9
  42. package/plugins/lisa/skills/linear-prd-intake/SKILL.md +23 -13
  43. package/plugins/lisa/skills/linear-prd-intake/agents/openai.yaml +2 -2
  44. package/plugins/lisa/skills/linear-write-issue/SKILL.md +10 -2
  45. package/plugins/lisa/skills/linear-write-prd/SKILL.md +90 -0
  46. package/plugins/lisa/skills/linear-write-prd/agents/openai.yaml +4 -0
  47. package/plugins/lisa/skills/notion-access/SKILL.md +2 -0
  48. package/plugins/lisa/skills/notion-prd-intake/SKILL.md +22 -12
  49. package/plugins/lisa/skills/notion-prd-intake/agents/openai.yaml +2 -2
  50. package/plugins/lisa/skills/notion-write-prd/SKILL.md +107 -0
  51. package/plugins/lisa/skills/notion-write-prd/agents/openai.yaml +4 -0
  52. package/plugins/lisa/skills/prd-source-write/SKILL.md +80 -0
  53. package/plugins/lisa/skills/prd-source-write/agents/openai.yaml +4 -0
  54. package/plugins/lisa/skills/project-ideation/SKILL.md +183 -80
  55. package/plugins/lisa/skills/repair-intake/SKILL.md +403 -0
  56. package/plugins/lisa/skills/repair-intake/agents/openai.yaml +4 -0
  57. package/plugins/lisa/skills/research/SKILL.md +19 -3
  58. package/plugins/lisa/skills/research/agents/openai.yaml +2 -2
  59. package/plugins/lisa/skills/setup-automations/SKILL.md +78 -0
  60. package/plugins/lisa/skills/setup-automations/agents/openai.yaml +4 -0
  61. package/plugins/lisa/skills/setup-github/SKILL.md +0 -1
  62. package/plugins/lisa/skills/tear-down-automations/SKILL.md +34 -0
  63. package/plugins/lisa/skills/tear-down-automations/agents/openai.yaml +4 -0
  64. package/plugins/lisa/skills/tracker-build-intake/SKILL.md +6 -2
  65. package/plugins/lisa/skills/tracker-build-intake/agents/openai.yaml +2 -2
  66. package/plugins/lisa/skills/tracker-evidence/SKILL.md +2 -2
  67. package/plugins/lisa/skills/tracker-sync/SKILL.md +1 -1
  68. package/plugins/lisa/skills/verify-prd/SKILL.md +41 -38
  69. package/plugins/lisa-cdk/.claude-plugin/plugin.json +1 -1
  70. package/plugins/lisa-cdk/.codex-plugin/plugin.json +1 -1
  71. package/plugins/lisa-expo/.claude-plugin/plugin.json +1 -1
  72. package/plugins/lisa-expo/.codex-plugin/plugin.json +1 -1
  73. package/plugins/lisa-expo/commands/exploratory-qa.md +3 -3
  74. package/plugins/lisa-expo/skills/exploratory-qa/SKILL.md +48 -18
  75. package/plugins/lisa-expo/skills/exploratory-qa/agents/openai.yaml +2 -2
  76. package/plugins/lisa-harper-fabric/.claude-plugin/plugin.json +1 -1
  77. package/plugins/lisa-harper-fabric/.codex-plugin/plugin.json +1 -1
  78. package/plugins/lisa-harper-fabric/commands/exploratory-qa.md +3 -3
  79. package/plugins/lisa-harper-fabric/skills/exploratory-qa/SKILL.md +48 -18
  80. package/plugins/lisa-harper-fabric/skills/exploratory-qa/agents/openai.yaml +2 -2
  81. package/plugins/lisa-nestjs/.claude-plugin/plugin.json +1 -1
  82. package/plugins/lisa-nestjs/.codex-plugin/plugin.json +1 -1
  83. package/plugins/lisa-openclaw/.claude-plugin/plugin.json +1 -1
  84. package/plugins/lisa-openclaw/.codex-plugin/plugin.json +1 -1
  85. package/plugins/lisa-rails/.claude-plugin/plugin.json +1 -1
  86. package/plugins/lisa-rails/.codex-plugin/plugin.json +1 -1
  87. package/plugins/lisa-rails/commands/exploratory-qa.md +3 -3
  88. package/plugins/lisa-rails/skills/exploratory-qa/SKILL.md +48 -18
  89. package/plugins/lisa-rails/skills/exploratory-qa/agents/openai.yaml +2 -2
  90. package/plugins/lisa-typescript/.claude-plugin/plugin.json +1 -1
  91. package/plugins/lisa-typescript/.codex-plugin/plugin.json +1 -1
  92. package/plugins/lisa-wiki/.claude-plugin/plugin.json +1 -1
  93. package/plugins/lisa-wiki/.codex-plugin/plugin.json +1 -1
  94. package/plugins/lisa-wiki/skills/lisa-wiki-ingest/SKILL.md +30 -1
  95. package/plugins/src/base/agents/confluence-prd-intake.md +1 -1
  96. package/plugins/src/base/agents/github-agent.md +4 -5
  97. package/plugins/src/base/agents/github-build-intake.md +1 -1
  98. package/plugins/src/base/agents/github-prd-intake.md +1 -1
  99. package/plugins/src/base/agents/linear-prd-intake.md +1 -1
  100. package/plugins/src/base/agents/notion-prd-intake.md +1 -1
  101. package/plugins/src/base/commands/intake.md +1 -1
  102. package/plugins/src/base/commands/project-ideation.md +3 -3
  103. package/plugins/src/base/commands/repair-intake.md +6 -0
  104. package/plugins/src/base/commands/research.md +3 -3
  105. package/plugins/src/base/commands/setup-automations.md +6 -0
  106. package/plugins/src/base/commands/tear-down-automations.md +6 -0
  107. package/plugins/src/base/commands/verify-prd.md +2 -2
  108. package/plugins/src/base/rules/config-resolution.md +63 -4
  109. package/plugins/src/base/rules/intent-routing.md +4 -3
  110. package/plugins/src/base/rules/leaf-only-lifecycle.md +1 -1
  111. package/plugins/src/base/rules/prd-lifecycle-rollup.md +10 -2
  112. package/plugins/src/base/rules/repo-scope-split.md +18 -1
  113. package/plugins/src/base/skills/confluence-prd-intake/SKILL.md +24 -14
  114. package/plugins/src/base/skills/confluence-write-prd/SKILL.md +103 -0
  115. package/plugins/src/base/skills/github-build-intake/SKILL.md +32 -21
  116. package/plugins/src/base/skills/github-evidence/SKILL.md +3 -26
  117. package/plugins/src/base/skills/github-journey/SKILL.md +2 -2
  118. package/plugins/src/base/skills/github-prd-intake/SKILL.md +25 -11
  119. package/plugins/src/base/skills/github-sync/SKILL.md +5 -5
  120. package/plugins/src/base/skills/github-write-issue/SKILL.md +15 -6
  121. package/plugins/src/base/skills/github-write-prd/SKILL.md +100 -0
  122. package/plugins/src/base/skills/implement/SKILL.md +13 -6
  123. package/plugins/src/base/skills/intake/SKILL.md +13 -12
  124. package/plugins/src/base/skills/jira-build-intake/SKILL.md +24 -11
  125. package/plugins/src/base/skills/jira-write-ticket/SKILL.md +8 -0
  126. package/plugins/src/base/skills/linear-build-intake/SKILL.md +22 -9
  127. package/plugins/src/base/skills/linear-prd-intake/SKILL.md +23 -13
  128. package/plugins/src/base/skills/linear-write-issue/SKILL.md +10 -2
  129. package/plugins/src/base/skills/linear-write-prd/SKILL.md +90 -0
  130. package/plugins/src/base/skills/notion-access/SKILL.md +2 -0
  131. package/plugins/src/base/skills/notion-prd-intake/SKILL.md +22 -12
  132. package/plugins/src/base/skills/notion-write-prd/SKILL.md +107 -0
  133. package/plugins/src/base/skills/prd-source-write/SKILL.md +80 -0
  134. package/plugins/src/base/skills/project-ideation/SKILL.md +183 -80
  135. package/plugins/src/base/skills/repair-intake/SKILL.md +403 -0
  136. package/plugins/src/base/skills/research/SKILL.md +19 -3
  137. package/plugins/src/base/skills/setup-automations/SKILL.md +78 -0
  138. package/plugins/src/base/skills/setup-github/SKILL.md +0 -1
  139. package/plugins/src/base/skills/tear-down-automations/SKILL.md +34 -0
  140. package/plugins/src/base/skills/tracker-build-intake/SKILL.md +6 -2
  141. package/plugins/src/base/skills/tracker-evidence/SKILL.md +2 -2
  142. package/plugins/src/base/skills/tracker-sync/SKILL.md +1 -1
  143. package/plugins/src/base/skills/verify-prd/SKILL.md +41 -38
  144. package/plugins/src/expo/commands/exploratory-qa.md +3 -3
  145. package/plugins/src/expo/skills/exploratory-qa/SKILL.md +48 -18
  146. package/plugins/src/harper-fabric/commands/exploratory-qa.md +3 -3
  147. package/plugins/src/harper-fabric/skills/exploratory-qa/SKILL.md +48 -18
  148. package/plugins/src/rails/commands/exploratory-qa.md +3 -3
  149. package/plugins/src/rails/skills/exploratory-qa/SKILL.md +48 -18
  150. package/plugins/src/wiki/skills/lisa-wiki-ingest/SKILL.md +30 -1
@@ -203,7 +203,7 @@ If the item modifies an existing user-facing surface, a `lisa:product-walkthroug
203
203
 
204
204
  Before create/update, verify each field is populated where applicable:
205
205
 
206
- - **Labels**: include `status:ready` for new items; component labels (`component:<name>`); status / priority labels are NOT redundant with native fields — labels exist for portability and downstream queries.
206
+ - **Labels**: include `status:ready` for a new **leaf** work unit (Bug / Task / Sub-task / Improvement with no child work) per `leaf-only-lifecycle`, **unless `build_ready: false`** (see the Build-ready control input below); component labels (`component:<name>`); status / priority labels are NOT redundant with native fields — labels exist for portability and downstream queries. A container (Epic Project / Story with sub-issues / Spike) never receives `status:ready`.
207
207
  - **Native priority field**: 0–4 per Linear's scale; explicit, not "unset".
208
208
  - **Native estimate**: per Linear's team-configured estimate scale (often 0–8 Fibonacci); skip for Epic / Spike.
209
209
  - **ProjectMilestone**: when the team uses dated milestones, set the milestone on the Project (Epic) or on the Issue (when an Issue belongs to a milestone).
@@ -212,6 +212,14 @@ Before create/update, verify each field is populated where applicable:
212
212
 
213
213
  For Bug / Task / Sub-task, ensure the summary is prefixed with `[<repo-name>]`.
214
214
 
215
+ ### Build-ready control input (`build_ready`)
216
+
217
+ `build_ready` is an optional write-control input (default: **omitted**). It governs whether a **leaf** work unit's `status:ready` label is applied on create. It never overrides `leaf-only-lifecycle` — a container is never stamped build-ready regardless of `build_ready`. "Not build-ready" is not a special state: the Issue is still created with Linear's default native `Todo` state; it just lacks the `status:ready` **label** the build lifecycle keys off, so a human can promote it later.
218
+
219
+ - **Omitted** → current behavior: a leaf work unit receives `status:ready`. Preserves what every existing caller (`lisa:plan`, the `*-to-tracker` skills) relies on.
220
+ - **`build_ready: false`** → create the leaf **without** the `status:ready` label, so it sits in the backlog for a human to review and promote into the queue.
221
+ - **`build_ready: true`** → ensure the leaf carries `status:ready` so `lisa:intake` / `lisa:linear-build-intake` auto-picks it up.
222
+
215
223
  ## Phase 5.5 — Validate (Pre-write Gate)
216
224
 
217
225
  Before any write, invoke `lisa:linear-validate-issue` with the full proposed spec assembled from Phases 2 / 3 / 4 / 5. Pass it as a YAML block per the `lisa:linear-validate-issue` schema, including `runtime_behavior_change`, `authenticated_surface`, and `artifacts_attached` flags so the right gates run.
@@ -236,7 +244,7 @@ If the validator reports `PASS`, continue to Phase 6.
236
244
 
237
245
  ### CREATE — Story / Task / Bug / Spike / Improvement (Issue with projectId)
238
246
 
239
- 1. Resolve any required Issue labels (`status:ready`, `component:<name>`, `prd-intake-feedback` only if this is a sentinel issue, etc.) via `mcp__linear-server__list_issue_labels` (create via `create_issue_label` if missing).
247
+ 1. Resolve any required Issue labels (`component:<name>`, `prd-intake-feedback` only if this is a sentinel issue, etc.) via `mcp__linear-server__list_issue_labels` (create via `create_issue_label` if missing). Include `status:ready` in `labelIds` only for a **leaf** work unit and only when `build_ready` is not `false` (per the Build-ready control input) — omit it for a container, and for a `build_ready: false` leaf which then waits in the backlog for a human to promote it.
240
248
  2. Call `mcp__linear-server__save_issue` with: `team` (teamId), `title` (summary), `description` (markdown), `projectId` (the Epic Project), `priority` (0–4), `estimate`, `labelIds`, `assignee` if known.
241
249
  3. Capture the returned identifier (e.g. `ENG-123`) — Phase 4 sub-tasks need it as `parentId`.
242
250
  4. Add relationships from Phase 4b via `save_issue` (relations field) or paired relation calls.
@@ -0,0 +1,90 @@
1
+ ---
2
+ name: linear-write-prd
3
+ description: "Creates or idempotently updates a PRD as a Linear Project carrying exactly one PRD lifecycle project-label (`prd-draft` by default, or `prd-ready` when initial_role is ready so lisa:linear-prd-intake auto-claims it). The Linear PRD-source writer behind lisa:prd-source-write. Dedupes by a stable marker embedded in the Project description (matched by marker, never by name). Uses the Linear MCP."
4
+ allowed-tools: ["Skill", "Bash", "mcp__linear-server__list_teams", "mcp__linear-server__list_projects", "mcp__linear-server__get_project", "mcp__linear-server__save_project", "mcp__linear-server__list_project_labels", "mcp__linear-server__create_project_label"]
5
+ ---
6
+
7
+ # Write Linear PRD: $ARGUMENTS
8
+
9
+ Create (or update) a PRD as a Linear **Project** in the configured workspace/team. Invoked by
10
+ `lisa:prd-source-write` when `source = linear`; do not call directly from a vendor-neutral caller.
11
+
12
+ Linear's PRD lifecycle uses **project-level labels** (`prd-*`), per `config-resolution`. (The Linear
13
+ PRD source models a PRD as a Project — the same shape `lisa:linear-prd-intake` scans.)
14
+
15
+ `$ARGUMENTS` carries the `lisa:prd-source-write` spec: `title`, `body` (full PRD markdown),
16
+ `initial_role` (`draft` | `ready`, default `draft`), `dedupe_key`, `marker`, optional `source_ref`.
17
+
18
+ ## Phase 1 — Resolve workspace/team + PRD lifecycle labels
19
+
20
+ ```bash
21
+ read_g() { local lv gv; lv=$(jq -r "$1 // empty" .lisa.config.local.json 2>/dev/null); gv=$(jq -r "$1 // empty" .lisa.config.json 2>/dev/null); echo "${lv:-${gv:-$2}}"; }
22
+ WORKSPACE=$(read_g '.linear.workspace' '')
23
+ TEAM=$(read_g '.linear.teamKey' '')
24
+ # Resolve the FULL PRD lifecycle vocabulary from config (never hard-code names) so the one-of
25
+ # reconcile and the past-ready check are correct even when a project renamed any label.
26
+ PRD_DRAFT=$(read_g '.linear.labels.prd.draft' 'prd-draft')
27
+ PRD_READY=$(read_g '.linear.labels.prd.ready' 'prd-ready')
28
+ PRD_IN_REVIEW=$(read_g '.linear.labels.prd.in_review' 'prd-in-review')
29
+ PRD_BLOCKED=$(read_g '.linear.labels.prd.blocked' 'prd-blocked')
30
+ PRD_TICKETED=$(read_g '.linear.labels.prd.ticketed' 'prd-ticketed')
31
+ PRD_SHIPPED=$(read_g '.linear.labels.prd.shipped' 'prd-shipped')
32
+ PRD_VERIFIED=$(read_g '.linear.labels.prd.verified' 'prd-verified')
33
+ ALL_PRD_LABELS=("$PRD_DRAFT" "$PRD_READY" "$PRD_IN_REVIEW" "$PRD_BLOCKED" "$PRD_TICKETED" "$PRD_SHIPPED" "$PRD_VERIFIED")
34
+ PROGRESSED=("$PRD_IN_REVIEW" "$PRD_BLOCKED" "$PRD_TICKETED" "$PRD_SHIPPED" "$PRD_VERIFIED")
35
+ [ -z "$WORKSPACE" ] && { echo "Error: linear.workspace not set in .lisa.config.json."; exit 1; }
36
+ [ -z "$TEAM" ] && { echo "Error: linear.teamKey not set in .lisa.config.json. A team key is required to create or scope a Linear Project."; exit 1; }
37
+ ```
38
+
39
+ Resolve the target project-label from `initial_role`: `ready` → `$PRD_READY`, else `$PRD_DRAFT`.
40
+ Resolve its label id via `mcp__linear-server__list_project_labels` (create via
41
+ `mcp__linear-server__create_project_label` if missing).
42
+
43
+ ## Phase 2 — Dedupe by marker (search before create)
44
+
45
+ The `marker` is embedded in the Project description. Find an existing Project carrying it — match the
46
+ marker, **never** the project name:
47
+
48
+ 1. `mcp__linear-server__list_projects` scoped to the team/workspace (filtered by the `prd-*` label
49
+ set when supported), then inspect each candidate's description via
50
+ `mcp__linear-server__get_project` for the marker. If `source_ref` was passed, target it directly.
51
+ 2. If a Project with the marker exists → **update**; else → **create**.
52
+
53
+ ## Phase 3 — Create or update
54
+
55
+ **Marker normalization (both paths).** Before writing the `description`, ensure it contains
56
+ **exactly one** marker line — inject the marker if the synthesized description lacks it. **Never write
57
+ a markerless description** (including UPDATE / `source_ref`): that breaks future dedupe.
58
+
59
+ **CREATE:** `mcp__linear-server__save_project` with:
60
+ - `name`: `$TITLE`
61
+ - `description`: the marker-normalized full PRD markdown
62
+ - `teamIds`: `[<resolved team id>]`
63
+ - `labelIds`: `[<role label id>]` (exactly one PRD lifecycle label)
64
+ - `state`: Linear Project default (e.g. `backlog`)
65
+
66
+ **UPDATE** (existing project or `source_ref`): `save_project` with the project id and **only** the
67
+ changed fields — regenerate the marker-normalized `description`, and reconcile labels to **exactly
68
+ one** PRD lifecycle label: add the role label, remove every other label in the resolved
69
+ `${ALL_PRD_LABELS[@]}` set (config-resolved names, not a hard-coded list). Do **not** down-rank a
70
+ Project whose current label is in the resolved `${PROGRESSED[@]}` set (already past `ready`) — leave
71
+ it and report `reused (already past ready)`.
72
+
73
+ ## Phase 4 — Return
74
+
75
+ ```yaml
76
+ ref: "<linear-project-id-or-slug>"
77
+ url: "<project url>"
78
+ role: draft | ready # (or the Project's current role when reused past ready)
79
+ marker: "<MARKER>"
80
+ outcome: created | reused
81
+ ```
82
+
83
+ ## Rules
84
+
85
+ - Exactly one PRD lifecycle project-label at all times.
86
+ - Match dedupe by marker, never by project name.
87
+ - Never down-rank a Project already past `ready`.
88
+ - Source-side writer (`prd-*` project labels) — never touches issue-level build labels (`status:*`),
89
+ which are `lisa:linear-write-issue`'s lane (see `config-resolution` self-host separation).
90
+ - Resolve label names from config (`linear.labels.prd.*`) — never hardcode.
@@ -0,0 +1,4 @@
1
+ display_name: "Linear Write PRD"
2
+ short_description: "Creates or idempotently updates a PRD as a Linear Project carrying exactly one PRD lifecycle project-label (`prd-draft` by default, or…"
3
+ default_prompt:
4
+ - "Use $linear-write-prd: Creates or idempotently updates a PRD as a Linear Project carrying exactly one PRD lifecycle project-label (`prd-draft` by default, or…."
@@ -12,6 +12,7 @@ Single chokepoint for all Notion operations. Routes each op to a substrate, enfo
12
12
 
13
13
  ```text
14
14
  operation: read-page id: <uuid>
15
+ operation: create-page parent_database_id: <uuid> properties: {...} [children: [...]] # create a new page (e.g. a PRD row) in a database; children is optional — omit to create a page without initial block content
15
16
  operation: write-page payload: {...} # update page properties
16
17
  operation: archive-page id: <uuid>
17
18
  operation: query-database id: <uuid> filter: {...} sort: {...}
@@ -164,6 +165,7 @@ Substrate columns: try the column matching `$substrate` first. If that column is
164
165
  |---|---|---|
165
166
  | **Pages** | | |
166
167
  | `read-page id:<I>` | `mcp__claude_ai_Notion__notion-fetch` | `GET /v1/pages/<I>` |
168
+ | `create-page parent_database_id:<D> properties:<P> [children:<arr>]` | `mcp__claude_ai_Notion__notion-create-pages` | `POST /v1/pages` body `{ "parent": { "database_id": "<D>" }, "properties": <P>, "children": <arr?> }` (children optional per Notion API) |
167
169
  | `write-page payload:<P>` | `mcp__claude_ai_Notion__notion-update-page` | `PATCH /v1/pages/<I>` body `{ "properties": {...}, "archived": true/false }` |
168
170
  | `archive-page id:<I>` | `mcp__claude_ai_Notion__notion-update-page` (with `archived: true`) | `PATCH /v1/pages/<I>` body `{ "archived": true }` |
169
171
  | `append-blocks page_id:<P> children:<arr>` | (no direct equivalent) | `PATCH /v1/blocks/<P>/children` body `{ "children": <arr> }` |
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: notion-prd-intake
3
- description: "Scans a Notion PRD database for pages in the configured `ready` status and runs each one through the dry-run validation pipeline. PRDs that pass every gate get tickets written and the status flipped to the configured `ticketed` value; PRDs that fail get clarifying-question comments and the status flipped to the configured `blocked` value. The skill is the runtime for the ready → in_review → blocked|ticketed lifecycle. Composes existing skills (notion-to-tracker, tracker-validate, tracker-source-artifacts, product-walkthrough); does not reimplement their logic."
3
+ description: "Scans a Notion PRD database for pages in the configured `ready` status and runs the first eligible one through the dry-run validation pipeline. A PRD that passes every gate gets tickets written and the status flipped to the configured `ticketed` value; a PRD that fails gets clarifying-question comments and the status flipped to the configured `blocked` value. The skill is the runtime for the ready → in_review → blocked|ticketed lifecycle. Composes existing skills (notion-to-tracker, tracker-validate, tracker-source-artifacts, product-walkthrough); does not reimplement their logic."
4
4
  allowed-tools: ["Skill", "Bash", "Read", "Write", "Edit", "AskUserQuestion"]
5
5
  ---
6
6
 
@@ -14,7 +14,7 @@ allowed-tools: ["Skill", "Bash", "Read", "Write", "Edit", "AskUserQuestion"]
14
14
  https://www.notion.so/geminisports/28fd00244d7d47c5866876f7de48c0fe?v=34eba63a2800815891a3000c643f0ea8
15
15
  ```
16
16
 
17
- Run one intake cycle against that database. Each PRD in the configured `ready` status is claimed, validated, and routed to either `blocked` (with clarifying comments) or `ticketed` (with destination tickets created).
17
+ Run one intake cycle against that database. The first eligible PRD in the configured `ready` status is claimed, validated, routed to either `blocked` (with clarifying comments) or `ticketed` (with destination tickets created), then the cycle exits. Remaining ready PRDs stay queued for later scheduler invocations.
18
18
 
19
19
  ## Workflow resolution
20
20
 
@@ -56,7 +56,7 @@ This skill shares its PRD closure rollup phase (3f) with `lisa:github-prd-intake
56
56
 
57
57
  ## Confirmation policy
58
58
 
59
- Do NOT ask the caller whether to proceed. Once invoked with a database URL, run the cycle to completion — claim, validate, branch to `blocked` or `ticketed`, write the summary. The caller (a human or a cron) has already authorized the run by invoking the skill; re-prompting defeats the purpose of a background batch.
59
+ Do NOT ask the caller whether to proceed. Once invoked with a database URL, run the cycle to completion for the first eligible PRD — claim, validate, branch to `blocked` or `ticketed`, write the summary, and exit. The caller (a human or a cron) has already authorized the run by invoking the skill; re-prompting defeats the purpose of a background queue.
60
60
 
61
61
  Specifically forbidden:
62
62
 
@@ -82,7 +82,7 @@ draft → ready → in_review → blocked | ticketed → shipped → verified
82
82
 
83
83
  (Default status values: `Draft` / `Ready` / `In Review` / `Blocked` / `Ticketed` / `Shipped` / `Verified`.)
84
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.
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 does **not** use `blocked`; `/lisa:verify-prd` re-opens the PRD `shipped → ticketed` and creates build-ready fix tickets that auto-build and trigger a re-verify (the self-healing loop), introducing no `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
86
 
87
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).
88
88
 
@@ -110,9 +110,9 @@ For a `select`-type property substitute `"select"` for `"status"`. The response
110
110
 
111
111
  If the result set is empty, stop and report `"No PRDs with $STATUS_PROP=$READY. Nothing to do."` Exit cleanly — this is the common idle case for a scheduled run.
112
112
 
113
- ### Phase 3 — Process each ready PRD
113
+ ### Phase 3 — Process the first eligible ready PRD
114
114
 
115
- For each PRD page (process serially to keep status transitions auditable):
115
+ Select the first ready PRD page returned by Phase 2 and process only that page. Later scheduler invocations process the remaining ready PRDs.
116
116
 
117
117
  #### 3a. Claim
118
118
 
@@ -155,7 +155,7 @@ Per-ticket gates prove each ticket is well-formed; they do NOT prove the *set* o
155
155
 
156
156
  | Verdict | Action |
157
157
  |---------|--------|
158
- | `COMPLETE` | Done. Leave `$STATUS_PROP = $TICKETED`. Move to next PRD. |
158
+ | `COMPLETE` | Done. Leave `$STATUS_PROP = $TICKETED`. End the cycle. |
159
159
  | `COMPLETE_WITH_SCOPE_CREEP` | Post an advisory Notion comment naming the scope-creep tickets (so product can decide whether to close them as out-of-scope). Leave `$STATUS_PROP = $TICKETED`. |
160
160
  | `GAPS_FOUND` | The created ticket set is incomplete. (a) For each gap, post a Notion comment using the same product-facing template as Phase 3c.3 — block-anchored when `prd_anchor` is non-null, page-level otherwise; category badge from the gap's `category` field; `What's unclear` and `Recommendation` from the audit report's `what` and `recommendation` fields. Apply the same forbidden-language rules from Phase 3c.5. (b) Post one summary comment listing the tickets that *were* successfully created (so product knows what to keep vs. what to extend). (c) Transition `$STATUS_PROP` from `$TICKETED` back to `$BLOCKED` by invoking `lisa:notion-access` operation `write-page` with the blocked-status payload. |
161
161
  | `NO_TICKETS_FOUND` | Should not happen if step 2 succeeded. If it does, log it as an Error in the cycle summary and leave `$STATUS_PROP = $TICKETED` with a comment flagging the audit failure for human review. |
@@ -239,9 +239,9 @@ rich_text: <Notion rich_text array — the comment body>
239
239
 
240
240
  The access skill resolves a `prd_anchor` substring to the matching block ID by paging through the PRD's children and posts the comment with `discussion_id` or `parent: { block_id }` as appropriate. If `block_anchor` is `null`, the access skill posts a page-level comment via `parent: { page_id }`.
241
241
 
242
- #### 3d. Continue
242
+ #### 3d. Stop
243
243
 
244
- Move to the next ready PRD. One PRD failing does not affect others.
244
+ Stop immediately after the claimed PRD is ticketed, blocked, or recorded as an error.
245
245
 
246
246
  #### 3f. PRD closure rollup (config-gated)
247
247
 
@@ -298,9 +298,19 @@ The set of **required** children for the all-terminal check is the top-level chi
298
298
 
299
299
  This phase implements exactly one PRD-lifecycle hop — `$TICKETED → $SHIPPED` — and the optional config-gated archive that follows it. All terminal-state semantics, the generated-top-level-work boundary, and the dedupe-by-child-ref idempotency come from the `prd-lifecycle-rollup` rule; this skill is its Notion implementation, not a second source of truth.
300
300
 
301
+ #### 3g. PRD verification dispatch (close the loop on shipped PRDs)
302
+
303
+ `shipped` and `verified` are distinct facts about a PRD (see the `prd-lifecycle-rollup` rule's "PRD-level verification vs ticket verification" and "Closing the loop" sections). Rollup (3f) only reaches `$SHIPPED`; the `shipped → verified` (pass) / `shipped → ticketed` (fail) hops are owned by `/lisa:verify-prd`. This phase **closes that loop** by dispatching the initiative-level acceptance gate for shipped PRDs. It never performs the verification transition itself — the "never sets the verification outcome" invariant holds: `lisa:verify-prd`, not this skill, sets `verified` (or, on failure, re-opens the PRD to `ticketed`).
304
+
305
+ Re-query the PRDs currently in the `$SHIPPED` status via `lisa:notion-access` `operation: query-database` filtered on `$STATUS_PROP = $SHIPPED`. Pick the **first** one and invoke `lisa:verify-prd <PRD-page-url>`. Process **one shipped PRD per cycle** — `lisa:verify-prd` is a heavy full flow (spec-conformance + empirical verification + fix-issue creation), so it is bounded exactly like the single-ready-PRD claim in Phase 3; the scheduler drains the rest.
306
+
307
+ **Per-cycle combined bound:** each scheduler cycle dispatches at most one ready PRD (the Phase 3 single-ready-PRD claim) **and** at most one shipped PRD for verification (this Phase 3g dispatch), for a maximum of two PRD operations per cycle. Ready intake runs first (Phase 3), then shipped verify (Phase 3g).
308
+
309
+ `lisa:verify-prd` owns the outcome: on a CONFORMS verdict with all empirical checks passing it transitions `$SHIPPED → verified` and posts evidence; on a conformance miss or a failing/unavailable check it **re-opens the PRD `$SHIPPED → ticketed`** (never `blocked`) and creates **build-ready** fix tickets registered as the PRD's generated work, then posts a failure report — the fix tickets auto-build, rollup (3f) re-ships the PRD once they are terminal, and a later cycle re-verifies (the self-healing loop). Either branch moves the PRD out of `$SHIPPED`, so it is not re-picked this cycle; a PRD whose generated work is not actually terminal is guard-stopped by `lisa:verify-prd` (left `$SHIPPED`) — that is verify-prd's gate, not this skill's. A PRD archived on ship (`notion.rollup.closeOnShipped = true`) is out of the open verification queue. This phase, like 3f, is **behaviorally identical across all four intake skills** (`github-prd-intake`, `linear-prd-intake`, `notion-prd-intake`, `confluence-prd-intake`) — only the `$SHIPPED` query surface differs; keep them aligned. Record the dispatched PRD + verify-prd's verdict in the summary.
310
+
301
311
  ### Phase 4 — Summary report
302
312
 
303
- After processing every ready PRD, emit a summary:
313
+ After processing the single selected PRD, emit a summary:
304
314
 
305
315
  ```text
306
316
  ## notion-prd-intake summary
@@ -325,10 +335,10 @@ Print to the agent's output. Do not write this summary to Notion or the destinat
325
335
 
326
336
  ## Idempotency & safety
327
337
 
328
- - **Single-cycle scope**: this skill processes the ready set as it exists at the start of Phase 2. New ready PRDs added mid-cycle are picked up next run.
338
+ - **One item per cycle**: this skill processes the first eligible ready PRD from Phase 2, then exits. New or remaining ready PRDs are picked up by later scheduler invocations.
329
339
  - **No writes outside the lifecycle**: this skill only ever writes to the destination tracker via `lisa:notion-to-tracker` (which delegates to `lisa:tracker-write`), and only ever changes the Notion status property to `$IN_REVIEW`, `$BLOCKED`, `$TICKETED`, or `$SHIPPED` (the last via the rollup phase 3f only). It never edits PRD content, never touches `$DRAFT`, never deletes pages. It sets `$SHIPPED` and may archive the PRD page **only** through the config-gated rollup phase (3f).
330
340
  - **Claim-first ordering**: the status flip to `$IN_REVIEW` is set BEFORE validation runs, so a re-entrant call won't double-process.
331
- - **Failure isolation**: an exception processing one PRD must not stop the cycle. Catch, record under "Errors" in the summary, continue to the next PRD. The PRD that errored is left in `$IN_REVIEW` — the human investigates from there.
341
+ - **Failure handling**: an exception processing the selected PRD is caught and recorded under "Errors" in the summary, then the cycle exits. The PRD that errored is left in `$IN_REVIEW` — the human investigates from there.
332
342
  - **Rollup idempotency**: rollup (Phase 3f) is a no-op on a PRD already in `$STATUS_PROP = $SHIPPED` (and already archived when `closeOnShipped` is `true`) — no duplicate transition, no duplicate archive, no duplicate comment. The all-terminal condition is a pure function of the children's current states (deduped by child-ref identity), so recomputing it is safe to re-run. Archival NEVER precedes the all-terminal condition.
333
343
 
334
344
  ## Configuration
@@ -1,4 +1,4 @@
1
1
  display_name: "Notion PRD Intake"
2
- short_description: "Scans a Notion PRD database for pages in the configured `ready` status and runs each one through the dry-run validation pipeline"
2
+ short_description: "Scans a Notion PRD database for pages in the configured `ready` status and runs the first eligible one through the dry-run validation"
3
3
  default_prompt:
4
- - "Use $notion-prd-intake: Scans a Notion PRD database for pages in the configured `ready` status and runs each one through the dry-run validation pipeline."
4
+ - "Use $notion-prd-intake: Scans a Notion PRD database for pages in the configured `ready` status and runs the first eligible one through the dry-run validation…."
@@ -0,0 +1,107 @@
1
+ ---
2
+ name: notion-write-prd
3
+ description: "Creates or idempotently updates a PRD as a page in the configured Notion PRD database, setting the lifecycle Status property to the draft value by default (or the ready value when initial_role is ready so lisa:notion-prd-intake auto-claims it). The Notion PRD-source writer behind lisa:prd-source-write. Dedupes by a stable marker embedded in the page (matched by marker, never by title). All Notion access goes through lisa:notion-access — never call the Notion API or MCP directly."
4
+ allowed-tools: ["Skill", "Bash"]
5
+ ---
6
+
7
+ # Write Notion PRD: $ARGUMENTS
8
+
9
+ Create (or update) a PRD page in the configured Notion PRD database. Invoked by
10
+ `lisa:prd-source-write` when `source = notion`; do not call directly from a vendor-neutral caller.
11
+ **All Notion operations go through `lisa:notion-access`** (the access chokepoint) — never curl the
12
+ Notion API or call a `mcp__*notion*` tool yourself.
13
+
14
+ `$ARGUMENTS` carries the `lisa:prd-source-write` spec: `title`, `body` (full PRD markdown),
15
+ `initial_role` (`draft` | `ready`, default `draft`), `dedupe_key`, `marker`, optional `source_ref`.
16
+
17
+ ## Phase 1 — Resolve database + Status vocabulary
18
+
19
+ ```bash
20
+ read_g() { local lv gv; lv=$(jq -r "$1 // empty" .lisa.config.local.json 2>/dev/null); gv=$(jq -r "$1 // empty" .lisa.config.json 2>/dev/null); echo "${lv:-${gv:-$2}}"; }
21
+ PRD_DB=$(read_g '.notion.prdDatabaseId' '')
22
+ [ -z "$PRD_DB" ] && { echo "Error: notion.prdDatabaseId not set in .lisa.config.json."; exit 1; }
23
+ STATUS_PROP=$(read_g '.notion.statusProperty' 'Status')
24
+ # Resolve the FULL PRD Status vocabulary from config (never hard-code) so the past-ready check is
25
+ # correct even when a project renamed any Status value.
26
+ DRAFT=$(read_g '.notion.values.draft' 'Draft')
27
+ READY=$(read_g '.notion.values.ready' 'Ready')
28
+ IN_REVIEW=$(read_g '.notion.values.in_review' 'In Review')
29
+ BLOCKED=$(read_g '.notion.values.blocked' 'Blocked')
30
+ TICKETED=$(read_g '.notion.values.ticketed' 'Ticketed')
31
+ SHIPPED=$(read_g '.notion.values.shipped' 'Shipped')
32
+ VERIFIED=$(read_g '.notion.values.verified' 'Verified')
33
+ # "Progressed past ready" set (never down-rank): the resolved in_review/blocked/ticketed/shipped/verified.
34
+ PROGRESSED=("$IN_REVIEW" "$BLOCKED" "$TICKETED" "$SHIPPED" "$VERIFIED")
35
+ ```
36
+
37
+ Resolve the target Status value from `initial_role`: `ready` → `$READY`, otherwise `$DRAFT`.
38
+
39
+ ## Phase 2 — Dedupe by marker (search before create)
40
+
41
+ The `marker` is embedded in the page (as the first body block). Find an existing PRD page in the DB
42
+ carrying it — match the marker, **never** the title:
43
+
44
+ 1. `lisa:notion-access` `operation: search query: "<marker>"` (Notion indexes page content).
45
+ 2. Filter results to pages whose parent is `$PRD_DB`. If `source_ref` was passed, target that page
46
+ directly and skip the search.
47
+ 3. If a matching page is found, this is an **update** — reuse it. If none is found, **create**.
48
+ Note: Notion search is eventually consistent; if a just-created page isn't found yet, the marker
49
+ still lives in the page so a later run will dedupe — surface "dedupe degraded (search lag)" rather
50
+ than silently creating a duplicate when uncertain.
51
+
52
+ ## Phase 3 — Create or update
53
+
54
+ **Markdown → Notion blocks (conversion boundary).** Convert the PRD markdown to Notion block objects:
55
+ `#`/`##`/`###` → `heading_1/2/3`, paragraphs → `paragraph`, `-`/`*` → `bulleted_list_item`, `1.` →
56
+ `numbered_list_item`, fenced code → `code`. The Notion API caps a single request at **100 blocks**
57
+ and ~2000 characters of rich text per block: split long paragraphs across blocks, and if the PRD
58
+ exceeds 100 blocks, create the page with the first ≤100 blocks then add the remainder with batched
59
+ `operation: append-blocks` calls (≤100 each). When the MCP substrate is active, `create-page` may
60
+ accept the markdown content directly (it performs this conversion) — prefer that; the explicit block
61
+ conversion is the curl-substrate path.
62
+
63
+ **Marker normalization (both paths).** The page must always carry **exactly one** marker. On CREATE
64
+ the marker is the first body block; on UPDATE never remove it. Never write a markerless page.
65
+
66
+ **CREATE:**
67
+
68
+ 1. Build the page body as Notion blocks per the conversion above: the **first block is the marker**
69
+ (a paragraph/callout containing `<!-- $MARKER -->`), then the converted PRD blocks.
70
+ 2. Invoke `lisa:notion-access` `operation: create-page` with:
71
+ ```json
72
+ { "parent_database_id": "<PRD_DB>",
73
+ "properties": { "<title-prop>": { "title": [{ "text": { "content": "<TITLE>" } }] },
74
+ "<STATUS_PROP>": { "status": { "name": "<ROLE_VALUE>" } } },
75
+ "children": [ <marker block>, <PRD body blocks> ] }
76
+ ```
77
+ Use the DB's actual title property name (read it via `operation: read-database id: <PRD_DB>` if
78
+ unknown) and the correct property type for `$STATUS_PROP` (`status` vs `select`).
79
+ 3. Capture the returned page id + URL.
80
+
81
+ **UPDATE** (existing page or `source_ref`):
82
+
83
+ 1. Set the Status to the resolved role via `lisa:notion-access` `operation: write-page payload: { "id": "<page-id>", "properties": { "<STATUS_PROP>": { "status": { "name": "<ROLE_VALUE>" } } } }` — **unless** the page's current Status is in the resolved `${PROGRESSED[@]}` set (already past `ready`), in which case leave the Status and report `reused (already past ready)`.
84
+ 2. Refresh the canonical spec, not append-only notes: keep the existing marker block, then make the
85
+ managed PRD body current — archive the previously generated body blocks below the marker
86
+ (`operation: archive-page` is page-level, so for blocks delete via the blocks API through
87
+ `notion-access` or, where block deletion isn't available, replace their text in place) and
88
+ `operation: append-blocks` the regenerated blocks. Do not duplicate the whole spec as a dated
89
+ note, and never drop the marker.
90
+
91
+ ## Phase 4 — Return
92
+
93
+ ```yaml
94
+ ref: "<notion-page-id>"
95
+ url: "<page url>"
96
+ role: draft | ready # (or the page's current Status role when reused past ready)
97
+ marker: "<MARKER>"
98
+ outcome: created | reused
99
+ ```
100
+
101
+ ## Rules
102
+
103
+ - All access via `lisa:notion-access`; never touch the Notion API/MCP directly.
104
+ - Match dedupe by marker, never by title.
105
+ - Never down-rank a PRD whose Status is already past `ready`.
106
+ - Resolve the Status vocabulary from config (`notion.statusProperty`, `notion.values.*`) — never
107
+ hardcode value names.
@@ -0,0 +1,4 @@
1
+ display_name: "Notion Write PRD"
2
+ short_description: "Creates or idempotently updates a PRD as a page in the configured Notion PRD database, setting the lifecycle Status property to the draft…"
3
+ default_prompt:
4
+ - "Use $notion-write-prd: Creates or idempotently updates a PRD as a page in the configured Notion PRD database, setting the lifecycle Status property to the draft…."
@@ -0,0 +1,80 @@
1
+ ---
2
+ name: prd-source-write
3
+ description: "Vendor-neutral wrapper for creating (or idempotently updating) a PRD in the configured PRD source. The PRD-side sibling of lisa:tracker-write. Resolves `source` from .lisa.config.local.json first (then .lisa.config.json — local overrides global) and dispatches to lisa:notion-write-prd, lisa:confluence-write-prd, lisa:github-write-prd, or lisa:linear-write-prd. Callers (notably lisa:research) MUST invoke this skill instead of a vendor PRD writer directly — that is what makes the PRD source switchable per project. Accepts an `initial_role` of `draft` (default) or `ready` so a freshly created PRD either waits for human promotion or is immediately picked up by lisa:intake; and a stable dedupe marker so re-runs reference the existing PRD instead of creating a duplicate. The PRD lives in the source — there is no separate document artifact."
4
+ allowed-tools: ["Skill", "Bash", "Read"]
5
+ ---
6
+
7
+ # PRD Source Write: $ARGUMENTS
8
+
9
+ Thin dispatcher. Resolves the configured PRD `source` and delegates to the matching vendor PRD
10
+ writer, which owns the concrete create/update, the lifecycle-role application, and the marker-based
11
+ dedupe. This skill only routes — it never talks to a source API itself.
12
+
13
+ See the `config-resolution` rule for the full configuration schema and the PRD lifecycle roles.
14
+
15
+ ## Input contract
16
+
17
+ Callers pass a single structured spec:
18
+
19
+ ```yaml
20
+ operation: create_or_update # the only operation; create unless the marker already exists
21
+ title: "<PRD title>"
22
+ body: "<full PRD markdown — the entire spec>"
23
+ initial_role: draft | ready # default: draft. ready = picked up by lisa:intake's PRD scan
24
+ dedupe_key: "<stable-key>" # e.g. project-ideation's idea key
25
+ marker: "[lisa-project-ideation] idea=<stable-key>" # embedded in the PRD body for dedupe
26
+ origin: { tool: project-ideation | research | manual }
27
+ source_ref: "<optional existing PRD ref to force an update>"
28
+ ```
29
+
30
+ `initial_role` semantics are uniform across vendors (the role STRINGS resolve per vendor from
31
+ `config-resolution`):
32
+
33
+ - **`draft`** (default) → the PRD is created in the source's `draft` PRD role. It waits for a human
34
+ (or a later `ready` promotion) before any intake claims it.
35
+ - **`ready`** → the PRD is created in the source's `ready` PRD role (`prd-ready`), so the PRD-side of
36
+ `lisa:intake` / the `*-prd-intake` scanner auto-claims it on the next cycle.
37
+
38
+ There is no "omitted = legacy behavior" mode (unlike the ticket-side `build_ready`): there was no
39
+ prior PRD-source-write behavior to preserve, so omitted means `draft`.
40
+
41
+ ## Workflow
42
+
43
+ 1. **Resolve the source.** Read `.lisa.config.local.json` first (if present), then
44
+ `.lisa.config.json`. Local overrides global per key. Use `jq` — never hand-parse JSON.
45
+
46
+ ```bash
47
+ local_source=$(jq -r '.source // empty' .lisa.config.local.json 2>/dev/null)
48
+ global_source=$(jq -r '.source // empty' .lisa.config.json 2>/dev/null)
49
+ source="${local_source:-${global_source}}"
50
+ if [ -z "$source" ]; then
51
+ echo "Error: 'source' is not set in .lisa.config.json. A PRD source (notion / confluence / github / linear) is required to create a PRD. Run /lisa:setup:notion (or :confluence, :github, :linear)." >&2
52
+ exit 1
53
+ fi
54
+ ```
55
+
56
+ 2. **Validate the value and dispatch** (pass the spec verbatim):
57
+
58
+ - `notion` → confirm `notion.workspaceId` and `notion.prdDatabaseId` are present, then invoke
59
+ `lisa:notion-write-prd`.
60
+ - `confluence` → confirm `atlassian.cloudId` and (`confluence.spaceKey` or
61
+ `confluence.parents.draft`/`.ready`) are present, then invoke `lisa:confluence-write-prd`.
62
+ - `github` → confirm `github.org` and `github.repo` are present, then invoke
63
+ `lisa:github-write-prd`.
64
+ - `linear` → confirm `linear.workspace` (and team for project placement) is present, then invoke
65
+ `lisa:linear-write-prd`.
66
+ - `jira` → **stop and fail loudly**: `"source=jira is not a supported PRD source — config-resolution defines no JIRA PRD lifecycle roles. Use notion / confluence / github / linear, or set source accordingly."` (JIRA is a destination tracker, not a PRD source.)
67
+ - Any other value (including `file`) → stop and report: `"Unknown PRD source '<value>'. Expected one of: notion, confluence, github, linear."`
68
+
69
+ 3. **Surface the vendor writer's output unchanged.** It returns the created/reused PRD ref + URL,
70
+ the applied role (`draft`/`ready`), the dedupe marker, and whether it was created or reused.
71
+ Downstream callers (research, project-ideation) parse this — do not paraphrase.
72
+
73
+ ## Rules
74
+
75
+ - Never bypass dispatch — a vendor-neutral caller calling a `*-write-prd` skill directly defeats the
76
+ per-project source switch (exactly the `tracker-write` discipline, mirrored).
77
+ - Never accept a source outside `{notion, confluence, github, linear}`. `jira` and `file` fail loudly.
78
+ - Never mutate the spec between layers. The vendor writers define their own create/dedupe contract.
79
+ - Never invent a PRD lifecycle role string — resolve every role from `config-resolution` per vendor.
80
+ - Idempotency is the vendor writer's job (marker search before create); this shim only routes.
@@ -0,0 +1,4 @@
1
+ display_name: "PRD Source Write"
2
+ short_description: "Vendor-neutral wrapper for creating (or idempotently updating) a PRD in the configured PRD source"
3
+ default_prompt:
4
+ - "Use $prd-source-write: Vendor-neutral wrapper for creating (or idempotently updating) a PRD in the configured PRD source."