@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.
- 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/agents/confluence-prd-intake.md +1 -1
- package/plugins/lisa/agents/github-agent.md +4 -5
- package/plugins/lisa/agents/github-build-intake.md +1 -1
- package/plugins/lisa/agents/github-prd-intake.md +1 -1
- package/plugins/lisa/agents/linear-prd-intake.md +1 -1
- package/plugins/lisa/agents/notion-prd-intake.md +1 -1
- package/plugins/lisa/commands/intake.md +1 -1
- package/plugins/lisa/commands/project-ideation.md +3 -3
- package/plugins/lisa/commands/repair-intake.md +6 -0
- package/plugins/lisa/commands/research.md +3 -3
- package/plugins/lisa/commands/setup-automations.md +6 -0
- package/plugins/lisa/commands/tear-down-automations.md +6 -0
- package/plugins/lisa/commands/verify-prd.md +2 -2
- package/plugins/lisa/rules/config-resolution.md +63 -4
- package/plugins/lisa/rules/intent-routing.md +4 -3
- package/plugins/lisa/rules/leaf-only-lifecycle.md +1 -1
- package/plugins/lisa/rules/prd-lifecycle-rollup.md +10 -2
- package/plugins/lisa/rules/repo-scope-split.md +18 -1
- package/plugins/lisa/skills/confluence-prd-intake/SKILL.md +24 -14
- package/plugins/lisa/skills/confluence-prd-intake/agents/openai.yaml +2 -2
- package/plugins/lisa/skills/confluence-write-prd/SKILL.md +103 -0
- package/plugins/lisa/skills/confluence-write-prd/agents/openai.yaml +4 -0
- package/plugins/lisa/skills/github-build-intake/SKILL.md +32 -21
- package/plugins/lisa/skills/github-evidence/SKILL.md +3 -26
- package/plugins/lisa/skills/github-evidence/agents/openai.yaml +2 -2
- package/plugins/lisa/skills/github-journey/SKILL.md +2 -2
- package/plugins/lisa/skills/github-prd-intake/SKILL.md +25 -11
- package/plugins/lisa/skills/github-prd-intake/agents/openai.yaml +2 -2
- package/plugins/lisa/skills/github-sync/SKILL.md +5 -5
- package/plugins/lisa/skills/github-write-issue/SKILL.md +15 -6
- package/plugins/lisa/skills/github-write-prd/SKILL.md +100 -0
- package/plugins/lisa/skills/github-write-prd/agents/openai.yaml +4 -0
- package/plugins/lisa/skills/implement/SKILL.md +13 -6
- package/plugins/lisa/skills/intake/SKILL.md +13 -12
- package/plugins/lisa/skills/intake/agents/openai.yaml +2 -2
- package/plugins/lisa/skills/jira-build-intake/SKILL.md +24 -11
- package/plugins/lisa/skills/jira-write-ticket/SKILL.md +8 -0
- package/plugins/lisa/skills/linear-build-intake/SKILL.md +22 -9
- package/plugins/lisa/skills/linear-prd-intake/SKILL.md +23 -13
- package/plugins/lisa/skills/linear-prd-intake/agents/openai.yaml +2 -2
- package/plugins/lisa/skills/linear-write-issue/SKILL.md +10 -2
- package/plugins/lisa/skills/linear-write-prd/SKILL.md +90 -0
- package/plugins/lisa/skills/linear-write-prd/agents/openai.yaml +4 -0
- package/plugins/lisa/skills/notion-access/SKILL.md +2 -0
- package/plugins/lisa/skills/notion-prd-intake/SKILL.md +22 -12
- package/plugins/lisa/skills/notion-prd-intake/agents/openai.yaml +2 -2
- package/plugins/lisa/skills/notion-write-prd/SKILL.md +107 -0
- package/plugins/lisa/skills/notion-write-prd/agents/openai.yaml +4 -0
- package/plugins/lisa/skills/prd-source-write/SKILL.md +80 -0
- package/plugins/lisa/skills/prd-source-write/agents/openai.yaml +4 -0
- package/plugins/lisa/skills/project-ideation/SKILL.md +183 -80
- package/plugins/lisa/skills/repair-intake/SKILL.md +403 -0
- package/plugins/lisa/skills/repair-intake/agents/openai.yaml +4 -0
- package/plugins/lisa/skills/research/SKILL.md +19 -3
- package/plugins/lisa/skills/research/agents/openai.yaml +2 -2
- package/plugins/lisa/skills/setup-automations/SKILL.md +78 -0
- package/plugins/lisa/skills/setup-automations/agents/openai.yaml +4 -0
- package/plugins/lisa/skills/setup-github/SKILL.md +0 -1
- package/plugins/lisa/skills/tear-down-automations/SKILL.md +34 -0
- package/plugins/lisa/skills/tear-down-automations/agents/openai.yaml +4 -0
- package/plugins/lisa/skills/tracker-build-intake/SKILL.md +6 -2
- package/plugins/lisa/skills/tracker-build-intake/agents/openai.yaml +2 -2
- package/plugins/lisa/skills/tracker-evidence/SKILL.md +2 -2
- package/plugins/lisa/skills/tracker-sync/SKILL.md +1 -1
- package/plugins/lisa/skills/verify-prd/SKILL.md +41 -38
- 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-expo/commands/exploratory-qa.md +3 -3
- package/plugins/lisa-expo/skills/exploratory-qa/SKILL.md +48 -18
- package/plugins/lisa-expo/skills/exploratory-qa/agents/openai.yaml +2 -2
- 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-harper-fabric/commands/exploratory-qa.md +3 -3
- package/plugins/lisa-harper-fabric/skills/exploratory-qa/SKILL.md +48 -18
- package/plugins/lisa-harper-fabric/skills/exploratory-qa/agents/openai.yaml +2 -2
- 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-rails/commands/exploratory-qa.md +3 -3
- package/plugins/lisa-rails/skills/exploratory-qa/SKILL.md +48 -18
- package/plugins/lisa-rails/skills/exploratory-qa/agents/openai.yaml +2 -2
- 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/lisa-wiki/skills/lisa-wiki-ingest/SKILL.md +30 -1
- package/plugins/src/base/agents/confluence-prd-intake.md +1 -1
- package/plugins/src/base/agents/github-agent.md +4 -5
- package/plugins/src/base/agents/github-build-intake.md +1 -1
- package/plugins/src/base/agents/github-prd-intake.md +1 -1
- package/plugins/src/base/agents/linear-prd-intake.md +1 -1
- package/plugins/src/base/agents/notion-prd-intake.md +1 -1
- package/plugins/src/base/commands/intake.md +1 -1
- package/plugins/src/base/commands/project-ideation.md +3 -3
- package/plugins/src/base/commands/repair-intake.md +6 -0
- package/plugins/src/base/commands/research.md +3 -3
- package/plugins/src/base/commands/setup-automations.md +6 -0
- package/plugins/src/base/commands/tear-down-automations.md +6 -0
- package/plugins/src/base/commands/verify-prd.md +2 -2
- package/plugins/src/base/rules/config-resolution.md +63 -4
- package/plugins/src/base/rules/intent-routing.md +4 -3
- package/plugins/src/base/rules/leaf-only-lifecycle.md +1 -1
- package/plugins/src/base/rules/prd-lifecycle-rollup.md +10 -2
- package/plugins/src/base/rules/repo-scope-split.md +18 -1
- package/plugins/src/base/skills/confluence-prd-intake/SKILL.md +24 -14
- package/plugins/src/base/skills/confluence-write-prd/SKILL.md +103 -0
- package/plugins/src/base/skills/github-build-intake/SKILL.md +32 -21
- package/plugins/src/base/skills/github-evidence/SKILL.md +3 -26
- package/plugins/src/base/skills/github-journey/SKILL.md +2 -2
- package/plugins/src/base/skills/github-prd-intake/SKILL.md +25 -11
- package/plugins/src/base/skills/github-sync/SKILL.md +5 -5
- package/plugins/src/base/skills/github-write-issue/SKILL.md +15 -6
- package/plugins/src/base/skills/github-write-prd/SKILL.md +100 -0
- package/plugins/src/base/skills/implement/SKILL.md +13 -6
- package/plugins/src/base/skills/intake/SKILL.md +13 -12
- package/plugins/src/base/skills/jira-build-intake/SKILL.md +24 -11
- package/plugins/src/base/skills/jira-write-ticket/SKILL.md +8 -0
- package/plugins/src/base/skills/linear-build-intake/SKILL.md +22 -9
- package/plugins/src/base/skills/linear-prd-intake/SKILL.md +23 -13
- package/plugins/src/base/skills/linear-write-issue/SKILL.md +10 -2
- package/plugins/src/base/skills/linear-write-prd/SKILL.md +90 -0
- package/plugins/src/base/skills/notion-access/SKILL.md +2 -0
- package/plugins/src/base/skills/notion-prd-intake/SKILL.md +22 -12
- package/plugins/src/base/skills/notion-write-prd/SKILL.md +107 -0
- package/plugins/src/base/skills/prd-source-write/SKILL.md +80 -0
- package/plugins/src/base/skills/project-ideation/SKILL.md +183 -80
- package/plugins/src/base/skills/repair-intake/SKILL.md +403 -0
- package/plugins/src/base/skills/research/SKILL.md +19 -3
- package/plugins/src/base/skills/setup-automations/SKILL.md +78 -0
- package/plugins/src/base/skills/setup-github/SKILL.md +0 -1
- package/plugins/src/base/skills/tear-down-automations/SKILL.md +34 -0
- package/plugins/src/base/skills/tracker-build-intake/SKILL.md +6 -2
- package/plugins/src/base/skills/tracker-evidence/SKILL.md +2 -2
- package/plugins/src/base/skills/tracker-sync/SKILL.md +1 -1
- package/plugins/src/base/skills/verify-prd/SKILL.md +41 -38
- package/plugins/src/expo/commands/exploratory-qa.md +3 -3
- package/plugins/src/expo/skills/exploratory-qa/SKILL.md +48 -18
- package/plugins/src/harper-fabric/commands/exploratory-qa.md +3 -3
- package/plugins/src/harper-fabric/skills/exploratory-qa/SKILL.md +48 -18
- package/plugins/src/rails/commands/exploratory-qa.md +3 -3
- package/plugins/src/rails/skills/exploratory-qa/SKILL.md +48 -18
- package/plugins/src/wiki/skills/lisa-wiki-ingest/SKILL.md +30 -1
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: linear-build-intake
|
|
3
|
-
description: "Symmetric counterpart to lisa:jira-build-intake on the Linear side. Scans a Linear team for Issues carrying the configured `ready` build label, claims
|
|
3
|
+
description: "Symmetric counterpart to lisa:jira-build-intake on the Linear side. Scans a Linear team for Issues carrying the configured `ready` build label, claims the first eligible Issue by relabeling to the configured `claimed` label, runs the implementation/build flow via lisa:linear-agent, relabels to the configured `done` label on completion, then exits. Enforces the claim-time arm of the `leaf-only-lifecycle` rule: a parent/container with open child work (or a childless Epic/Story/Spike) that still carries a stale build-ready label is skipped or safe-blocked with a lifecycle-repair comment, never claimed. The `ready` label is the human-flipped signal that an Issue is truly ready for development — mirroring how Notion PRDs work Draft → Ready → (us) In Review → Blocked|Ticketed."
|
|
4
4
|
allowed-tools: ["Skill", "Bash", "mcp__linear-server__list_teams", "mcp__linear-server__list_issues", "mcp__linear-server__get_issue", "mcp__linear-server__save_issue", "mcp__linear-server__save_comment", "mcp__linear-server__list_issue_labels", "mcp__linear-server__create_issue_label"]
|
|
5
5
|
---
|
|
6
6
|
|
|
@@ -12,7 +12,7 @@ allowed-tools: ["Skill", "Bash", "mcp__linear-server__list_teams", "mcp__linear-
|
|
|
12
12
|
2. The literal token `linear` — falls back to `linear.teamKey` from `.lisa.config.json`.
|
|
13
13
|
3. A pre-built Linear MCP filter (advanced) — used as-is.
|
|
14
14
|
|
|
15
|
-
Run one build-intake cycle.
|
|
15
|
+
Run one build-intake cycle. The first eligible ready Issue is claimed, built via the `lisa:linear-agent` flow, relabeled to the configured `done` label on completion, then the cycle exits. Remaining ready Issues stay queued for later scheduler invocations.
|
|
16
16
|
|
|
17
17
|
This skill is the destination of the `lisa:tracker-build-intake` shim when `tracker = "linear"`.
|
|
18
18
|
|
|
@@ -79,7 +79,7 @@ Reads `linear.workspace`, `linear.teamKey`, and `linear.labels.build.*` from `.l
|
|
|
79
79
|
|
|
80
80
|
## Confirmation policy
|
|
81
81
|
|
|
82
|
-
Do NOT ask the caller whether to proceed. Once invoked with a team key, run the cycle to completion — claim
|
|
82
|
+
Do NOT ask the caller whether to proceed. Once invoked with a team key, run the cycle to completion — claim and dispatch the first eligible Issue through `lisa:linear-agent`, transition a successful build to `$DONE`, write the summary, and exit. The caller (a human or a cron) has already authorized the run by invoking the skill.
|
|
83
83
|
|
|
84
84
|
Specifically forbidden:
|
|
85
85
|
|
|
@@ -126,13 +126,26 @@ Capture each Issue's: identifier, title, type label, priority, assignee, project
|
|
|
126
126
|
|
|
127
127
|
If empty, report `"No Linear Issues labeled $READY. Nothing to do."` and exit. Common idle case.
|
|
128
128
|
|
|
129
|
-
### Phase 3 — Process
|
|
129
|
+
### Phase 3 — Process the first eligible ready Issue
|
|
130
|
+
|
|
131
|
+
#### 3a.0 Repo-scope gate (claim only current-repo Issues)
|
|
132
|
+
|
|
133
|
+
A Linear team can oversee multiple repos (`frontend` / `backend` / `infrastructure`). This skill claims only Issues for the repo it is running in. Run this gate **before** the leaf-only gate (3a) and the claim (3b), per the `repo-scope-split` rule's "Claim-time repo scoping" section (cite it by slug; do not restate its decision table).
|
|
134
|
+
|
|
135
|
+
1. **Resolve the current repo** per `config-resolution` "Repo scoping" (`.repo` → `.github.repo` → `git remote get-url origin` basename). If unresolvable, stop and report.
|
|
136
|
+
2. **Cheap path first.** Prefer candidates already carrying the `repo:<current>` label. Keep the Phase 2 scan broad so unlabeled Issues are still seen, determined, and stamped.
|
|
137
|
+
3. **Per candidate, apply the repo-scope decision (`repo-scope-split`):**
|
|
138
|
+
- Carries `repo:<other>` → **skip** (leave it `ready` for that repo's own intake); next candidate.
|
|
139
|
+
- **Unlabeled** → determine the target repo(s) from the Issue + code surfaces, then **stamp** `repo:<name>` via `mcp__linear-server__save_issue` (resolve/create the label via `list_issue_labels`/`create_issue_label`) so later cycles filter cheaply; re-apply with the now-known repo.
|
|
140
|
+
- **Multi-repo leaf → split, never claim.** Run the `repo-scope-split` work-time procedure into single-repo siblings, each created **build-ready** (`build_ready: true`) and stamped with its own `repo:<name>`; the current repo's sibling becomes a normal candidate.
|
|
141
|
+
- **Single-repo leaf for the current repo** → fall through to 3a (leaf-only gate) and 3b (claim).
|
|
142
|
+
4. Continue until a claimable current-repo leaf is found (claim it; one per cycle) or the ready set is exhausted — exit cleanly with `"No ready Issues for repo <current>. Nothing to do."`.
|
|
130
143
|
|
|
131
144
|
#### 3a. Leaf-only claim gate (skip / safe-block containers)
|
|
132
145
|
|
|
133
146
|
Build intake claims **only independently implementable leaf work units**. This enforces the claim-time arm of the vendor-neutral `leaf-only-lifecycle` rule: a parent/container that still carries a stale build-ready label (e.g. `status:ready` applied before this rule existed, or hand-applied to a Project-grouped parent Issue) is **never claimed** — intake skips it or safe-blocks it with a clear lifecycle-repair message. It is the claim-time complement to the write-time labeling in `lisa:linear-write-issue` and the validate-time S15 gate in `lisa:linear-validate-issue`; all three cite the same rule so the classification never drifts. **Never silently implement a container.**
|
|
134
147
|
|
|
135
|
-
Run this gate **before** the claim relabel,
|
|
148
|
+
Run this gate **before** the claim relabel, starting with the oldest/highest-priority ready candidate. Do NOT relabel, comment "Claimed", or invoke `lisa:linear-agent` for an Issue that fails the gate.
|
|
136
149
|
|
|
137
150
|
**Resolve container vs. leaf — structural first, then nominal.** Per `leaf-only-lifecycle` the classification is structural: an Issue is a **container** if it has **open** child work, whatever its declared type; otherwise the **type label** decides. Resolve child work using the same hierarchy `lisa:linear-read-issue` uses — Linear's native parentage: an Issue groups **sub-issues** via `parentId`, and a **Project** (the Epic equivalent) groups Issues via `projectId`. Relations (`save_issue_relation` — `blocks` / `is blocked by`) express dependencies and are **not** parentage — do not count them as children.
|
|
138
151
|
|
|
@@ -159,7 +172,7 @@ Classify and act (first match wins). The type comes from the Issue's `type:` lab
|
|
|
159
172
|
|
|
160
173
|
The childless-parent exception is narrow: childlessness enables a claim **only** for types that are leaf work units to begin with. A childless Epic/Story/Spike is an incomplete decomposition, not an implementable unit — it is never claimed.
|
|
161
174
|
|
|
162
|
-
**Safe-block (default action for a flagged container).** Leave the build-ready label in place (don't silently strip it — that hides the lifecycle error), post a single lifecycle-repair comment,
|
|
175
|
+
**Safe-block (default action for a flagged container).** Leave the build-ready label in place (don't silently strip it — that hides the lifecycle error), post a single lifecycle-repair comment, record the Issue under "Skipped (container)" in the summary, and end the cycle. Do NOT relabel to `$CLAIMED`. Keep the comment idempotent — skip posting if an identical `[claude-build-intake]` lifecycle-repair comment already exists on the Issue, so a re-entrant cycle doesn't spam it.
|
|
163
176
|
|
|
164
177
|
Post via `mcp__linear-server__save_comment` with:
|
|
165
178
|
|
|
@@ -209,9 +222,9 @@ If `lisa:linear-agent` returned Success:
|
|
|
209
222
|
|
|
210
223
|
For any non-Success outcome, do NOT transition. The Issue sits where the agent left it — humans take it from there.
|
|
211
224
|
|
|
212
|
-
#### 3e.
|
|
225
|
+
#### 3e. Stop
|
|
213
226
|
|
|
214
|
-
|
|
227
|
+
Stop immediately after the first claimed, skipped, blocked, held, or errored Issue. Later scheduler invocations process the remaining ready Issues.
|
|
215
228
|
|
|
216
229
|
### Phase 4 — Summary report
|
|
217
230
|
|
|
@@ -243,7 +256,7 @@ Total PRs opened: <n>
|
|
|
243
256
|
- **Claim-first ordering**: `$CLAIMED` set BEFORE agent invocation — no double-pickup.
|
|
244
257
|
- **No writes outside the lifecycle**: this skill only adds/removes `$READY`, `$CLAIMED`, `$DONE`, plus terminal-only native state completion required by `leaf-only-lifecycle`. Every other label change (and non-terminal native state change) is owned by the agent or `lisa:linear-evidence`.
|
|
245
258
|
- **Terminal native closure**: after the `$DONE` label is applied, move the Linear Issue to a native completed state only when `$DONE` is the true terminal done value; intermediate env labels stay open / active.
|
|
246
|
-
- **
|
|
259
|
+
- **One item per cycle**: per-Issue exceptions are caught and recorded, then the cycle exits. The scheduler owns retrying or moving on to the next ready item.
|
|
247
260
|
- **Single cycle per team**: do not run two concurrent cycles against the same team — concurrent claims could race.
|
|
248
261
|
- **Single-label invariant**: after every transition, verify exactly one `status:*` label is present. Two simultaneously breaks the build queue.
|
|
249
262
|
- **Never pick an arbitrary env for `$DONE`**. If `done` is a map and env is ambiguous, fail loudly.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: linear-prd-intake
|
|
3
|
-
description: "Scans a Linear workspace (or a specific team) for projects carrying the configured `ready` PRD label and runs
|
|
3
|
+
description: "Scans a Linear workspace (or a specific team) for projects carrying the configured `ready` PRD label and runs the first eligible one through the dry-run validation pipeline. A project that passes every gate gets tickets written and the label flipped to the configured `ticketed` label; a project that fails gets clarifying-question comments (on a sentinel feedback issue under the project) and the label flipped to the configured `blocked` label. Linear counterpart of `lisa:notion-prd-intake` and `lisa:confluence-prd-intake` — the workflow is identical; only the source-of-truth tools differ. Composes existing skills (linear-to-tracker, tracker-validate, tracker-source-artifacts, product-walkthrough)."
|
|
4
4
|
allowed-tools: ["Skill", "Bash", "mcp__linear-server__list_projects", "mcp__linear-server__get_project", "mcp__linear-server__save_project", "mcp__linear-server__list_project_labels", "mcp__linear-server__list_issues", "mcp__linear-server__get_issue", "mcp__linear-server__save_issue", "mcp__linear-server__list_comments", "mcp__linear-server__save_comment", "mcp__linear-server__list_issue_labels", "mcp__linear-server__create_issue_label", "mcp__linear-server__list_documents", "mcp__linear-server__get_document", "mcp__linear-server__list_teams"]
|
|
5
5
|
---
|
|
6
6
|
|
|
@@ -12,7 +12,7 @@ allowed-tools: ["Skill", "Bash", "mcp__linear-server__list_projects", "mcp__line
|
|
|
12
12
|
- A Linear **team** URL or team key — scans every project on the team whose labels include the configured `ready` label. Example: `https://linear.app/acme/team/ENG/projects` or bare `ENG`.
|
|
13
13
|
- The literal token `linear` — equivalent to "the default Linear workspace"; only valid if `linear.workspace` is configured in `.lisa.config.json`.
|
|
14
14
|
|
|
15
|
-
Run one intake cycle against that scope.
|
|
15
|
+
Run one intake cycle against that scope. The first eligible project with the `ready` label is claimed, validated, routed to either the `blocked` label (with clarifying comments on a sentinel feedback issue) or the `ticketed` label (with destination tickets created), then the cycle exits. Remaining ready projects stay queued for later scheduler invocations.
|
|
16
16
|
|
|
17
17
|
## Workflow resolution
|
|
18
18
|
|
|
@@ -55,7 +55,7 @@ The **PRD closure rollup phase (3f)** transitions a `$TICKETED` PRD project to `
|
|
|
55
55
|
|
|
56
56
|
## Confirmation policy
|
|
57
57
|
|
|
58
|
-
Do NOT ask the caller whether to proceed. Once invoked with a workspace/team scope, 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
|
|
58
|
+
Do NOT ask the caller whether to proceed. Once invoked with a workspace/team scope, run the cycle to completion for the first eligible project — 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.
|
|
59
59
|
|
|
60
60
|
Specifically forbidden:
|
|
61
61
|
|
|
@@ -81,7 +81,7 @@ draft → ready → in_review → blocked | ticketed → shipped → verified
|
|
|
81
81
|
|
|
82
82
|
(Defaults: `prd-draft` / `prd-ready` / `prd-in-review` / `prd-blocked` / `prd-ticketed` / `prd-shipped` / `prd-verified`.)
|
|
83
83
|
|
|
84
|
-
`verified` is the terminal state after `shipped`: it means the shipped product has been empirically checked against the PRD (set by `/lisa:verify-prd`, not by this intake skill). A failed post-ship verification
|
|
84
|
+
`verified` is the terminal state after `shipped`: it means the shipped product has been empirically checked against the PRD (set by `/lisa:verify-prd`, not by this intake skill). A failed post-ship verification 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` state. Like `draft` and `shipped`, `verified` is **product-owned** — this intake skill never sets, clears, or otherwise touches it. See the "PRD-level verification vs ticket verification" section of the `prd-lifecycle-rollup` rule.
|
|
85
85
|
|
|
86
86
|
This skill transitions:
|
|
87
87
|
|
|
@@ -128,9 +128,9 @@ If the secondary query shows zero projects carrying any PRD lifecycle label →
|
|
|
128
128
|
|
|
129
129
|
If the secondary query shows projects with other PRD lifecycle labels but none with `$READY` → the queue is genuinely empty (all PRDs are already in `in_review`, `blocked`, `ticketed`, or `shipped`). Exit cleanly with `"No Linear projects labelled $READY. Nothing to do."`
|
|
130
130
|
|
|
131
|
-
### Phase 3 — Process
|
|
131
|
+
### Phase 3 — Process the first eligible ready PRD
|
|
132
132
|
|
|
133
|
-
|
|
133
|
+
Select the first ready project returned by Phase 2 and process only that project. Later scheduler invocations process the remaining ready projects.
|
|
134
134
|
|
|
135
135
|
#### 3a. Claim
|
|
136
136
|
|
|
@@ -219,9 +219,9 @@ Use these exact badge labels — they are the validator's category values transl
|
|
|
219
219
|
|
|
220
220
|
After all comments are posted (anchored groups + the optional sentinel-issue summary), transition labels: remove `$IN_REVIEW`, add `$BLOCKED` via `save_project`. Do NOT write any destination tickets.
|
|
221
221
|
|
|
222
|
-
#### 3d.
|
|
222
|
+
#### 3d. Stop
|
|
223
223
|
|
|
224
|
-
|
|
224
|
+
Stop immediately after the claimed PRD is ticketed, blocked, or recorded as an error.
|
|
225
225
|
|
|
226
226
|
#### 3e. Coverage audit (mandatory after $TICKETED)
|
|
227
227
|
|
|
@@ -232,7 +232,7 @@ Per-ticket gates prove each ticket is well-formed; they do NOT prove the *set* o
|
|
|
232
232
|
|
|
233
233
|
| Verdict | Action |
|
|
234
234
|
|---------|--------|
|
|
235
|
-
| `COMPLETE` | Done. Leave label as `$TICKETED`.
|
|
235
|
+
| `COMPLETE` | Done. Leave label as `$TICKETED`. End the cycle. |
|
|
236
236
|
| `COMPLETE_WITH_SCOPE_CREEP` | Post an advisory comment on the sentinel feedback issue naming the scope-creep tickets (so product can decide whether to close them as out-of-scope). Leave label as `$TICKETED`. |
|
|
237
237
|
| `GAPS_FOUND` | The created ticket set is incomplete. (a) For each gap, post a comment using the same product-facing template as Phase 3c.3 — anchored on the relevant sub-issue when `prd_anchor` is non-null, on the sentinel feedback issue 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 on the sentinel feedback issue listing the tickets that *were* successfully created (so product knows what to keep vs. what to extend). (c) Transition labels from `$TICKETED` back to `$BLOCKED` via `save_project`. |
|
|
238
238
|
| `NO_TICKETS_FOUND` | Should not happen if step 2 succeeded. If it does, log it as an Error in the cycle summary and leave label as `$TICKETED` with a comment flagging the audit failure for human review. |
|
|
@@ -296,9 +296,19 @@ The set of **required** children for the all-terminal check is the top-level chi
|
|
|
296
296
|
|
|
297
297
|
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 Linear implementation, not a second source of truth.
|
|
298
298
|
|
|
299
|
+
#### 3g. PRD verification dispatch (close the loop on shipped PRDs)
|
|
300
|
+
|
|
301
|
+
`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`).
|
|
302
|
+
|
|
303
|
+
Re-query the projects currently carrying the `$SHIPPED` label via `mcp__linear-server__list_projects` (filtered by the `$SHIPPED` project label, **including archived projects** — so PRDs archived on ship via `linear.labels.prd.rollup.closeOnShipped = true` are still dispatched to `lisa:verify-prd` for the shipped→verified progression). Pick the **first** one and invoke `lisa:verify-prd <project-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.
|
|
304
|
+
|
|
305
|
+
**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).
|
|
306
|
+
|
|
307
|
+
`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. 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.
|
|
308
|
+
|
|
299
309
|
### Phase 4 — Summary report
|
|
300
310
|
|
|
301
|
-
After processing
|
|
311
|
+
After processing the single selected PRD, emit a summary:
|
|
302
312
|
|
|
303
313
|
```text
|
|
304
314
|
## linear-prd-intake summary
|
|
@@ -341,10 +351,10 @@ Idempotency: the helper finds-or-creates. Re-runs of the cycle reuse the same se
|
|
|
341
351
|
|
|
342
352
|
## Idempotency & safety
|
|
343
353
|
|
|
344
|
-
- **
|
|
354
|
+
- **One item per cycle**: this skill processes the first eligible ready project from Phase 2, then exits. New or remaining `$READY` projects are picked up by later scheduler invocations.
|
|
345
355
|
- **No writes outside the lifecycle**: this skill only ever writes to the destination tracker via `lisa:linear-to-tracker` (which delegates to `lisa:tracker-write`), only ever changes Linear project labels among `$IN_REVIEW`, `$BLOCKED`, `$TICKETED`, and `$SHIPPED` (the last via the rollup phase 3f only), only ever creates/comments on the sentinel feedback issue (never any other Linear issue). It never edits project descriptions, never edits Linear documents, never touches the `draft` label, and never deletes projects. It sets the `$SHIPPED` label and may archive the PRD project **only** through the config-gated rollup phase (3f).
|
|
346
356
|
- **Claim-first ordering**: the label flip to `$IN_REVIEW` happens BEFORE validation runs, so a re-entrant call won't double-process.
|
|
347
|
-
- **Failure
|
|
357
|
+
- **Failure handling**: an exception processing the selected project is caught and recorded under "Errors" in the summary, then the cycle exits. The project that errored is left labelled `$IN_REVIEW` — the human investigates from there.
|
|
348
358
|
- **Single-label invariant**: after every transition, verify exactly one lifecycle label is present on the project. If two are present (rare race), surface as an Error and skip — do NOT auto-resolve, the human decides.
|
|
349
359
|
- **Rollup idempotency**: rollup (Phase 3f) is a no-op on a PRD project already carrying `$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.
|
|
350
360
|
|
|
@@ -388,4 +398,4 @@ Before this skill can run against a Linear workspace or team, the team must adop
|
|
|
388
398
|
3. (Optional but recommended) Add the `draft` and `shipped` labels (defaults: `prd-draft`, `prd-shipped`) for in-progress PRDs and delivered work respectively, so the full lifecycle is visible at a glance.
|
|
389
399
|
4. The labels must exist as **project labels** in Linear (`list_project_labels` should return them). Issue-level labels with the same names won't work; Linear keeps the two label kinds separate.
|
|
390
400
|
|
|
391
|
-
If the workspace hasn't adopted these labels, the first run exits with a label-convention error (not the idle empty-set message) — this distinguishes a setup issue from a genuinely empty queue so operators know to apply the convention rather than assuming
|
|
401
|
+
If the workspace hasn't adopted these labels, the first run exits with a label-convention error (not the idle empty-set message) — this distinguishes a setup issue from a genuinely empty queue so operators know to apply the convention rather than assuming there is no work. See Phase 2 for how the skill detects this case.
|
|
@@ -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
|
|
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 (`
|
|
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.
|
|
@@ -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
|
|
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.
|
|
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
|
|
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
|
|
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
|
|
113
|
+
### Phase 3 — Process the first eligible ready PRD
|
|
114
114
|
|
|
115
|
-
|
|
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`.
|
|
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.
|
|
242
|
+
#### 3d. Stop
|
|
243
243
|
|
|
244
|
-
|
|
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
|
|
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
|
-
- **
|
|
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
|
|
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
|
|
@@ -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.
|