@codyswann/lisa 2.21.1 → 2.23.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (152) hide show
  1. package/package.json +3 -2
  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 +11 -9
  5. package/plugins/lisa/agents/github-agent.md +18 -10
  6. package/plugins/lisa/agents/github-build-intake.md +10 -8
  7. package/plugins/lisa/agents/github-prd-intake.md +11 -9
  8. package/plugins/lisa/agents/jira-agent.md +12 -8
  9. package/plugins/lisa/agents/jira-build-intake.md +9 -7
  10. package/plugins/lisa/agents/learnings-synthesizer.md +1 -1
  11. package/plugins/lisa/agents/linear-agent.md +15 -9
  12. package/plugins/lisa/agents/linear-build-intake.md +13 -11
  13. package/plugins/lisa/agents/linear-prd-intake.md +11 -9
  14. package/plugins/lisa/agents/notion-prd-intake.md +11 -9
  15. package/plugins/lisa/agents/pr-mining-specialist.md +1 -1
  16. package/plugins/lisa/agents/tracker-mining-specialist.md +1 -1
  17. package/plugins/lisa/commands/setup/atlassian.md +7 -0
  18. package/plugins/lisa/commands/setup/confluence.md +7 -0
  19. package/plugins/lisa/commands/setup/jira.md +7 -0
  20. package/plugins/lisa/commands/setup/notion.md +7 -0
  21. package/plugins/lisa/hooks/enforce-team-first.sh +14 -6
  22. package/plugins/lisa/rules/base-rules.md +3 -3
  23. package/plugins/lisa/rules/config-resolution.md +242 -24
  24. package/plugins/lisa/rules/intent-routing.md +4 -4
  25. package/plugins/lisa/rules/repo-scope-split.md +41 -0
  26. package/plugins/lisa/rules/verification.md +13 -0
  27. package/plugins/lisa/skills/atlassian-access/SKILL.md +260 -0
  28. package/plugins/lisa/skills/confluence-prd-intake/SKILL.md +167 -82
  29. package/plugins/lisa/skills/confluence-to-tracker/SKILL.md +39 -26
  30. package/plugins/lisa/skills/debrief/SKILL.md +4 -5
  31. package/plugins/lisa/skills/github-add-journey/SKILL.md +1 -0
  32. package/plugins/lisa/skills/github-build-intake/SKILL.md +104 -40
  33. package/plugins/lisa/skills/github-evidence/SKILL.md +22 -5
  34. package/plugins/lisa/skills/github-prd-intake/SKILL.md +87 -51
  35. package/plugins/lisa/skills/github-to-tracker/SKILL.md +2 -2
  36. package/plugins/lisa/skills/github-validate-issue/SKILL.md +11 -1
  37. package/plugins/lisa/skills/implement/SKILL.md +5 -6
  38. package/plugins/lisa/skills/intake/SKILL.md +5 -6
  39. package/plugins/lisa/skills/jira-add-journey/SKILL.md +1 -0
  40. package/plugins/lisa/skills/jira-build-intake/SKILL.md +110 -45
  41. package/plugins/lisa/skills/jira-create/SKILL.md +5 -3
  42. package/plugins/lisa/skills/jira-evidence/SKILL.md +19 -2
  43. package/plugins/lisa/skills/jira-journey/SKILL.md +3 -1
  44. package/plugins/lisa/skills/jira-read-ticket/SKILL.md +10 -8
  45. package/plugins/lisa/skills/jira-sync/SKILL.md +11 -5
  46. package/plugins/lisa/skills/jira-validate-ticket/SKILL.md +22 -10
  47. package/plugins/lisa/skills/jira-verify/SKILL.md +5 -3
  48. package/plugins/lisa/skills/jira-write-ticket/SKILL.md +16 -14
  49. package/plugins/lisa/skills/linear-add-journey/SKILL.md +1 -0
  50. package/plugins/lisa/skills/linear-build-intake/SKILL.md +90 -32
  51. package/plugins/lisa/skills/linear-evidence/SKILL.md +22 -5
  52. package/plugins/lisa/skills/linear-prd-intake/SKILL.md +92 -57
  53. package/plugins/lisa/skills/linear-validate-issue/SKILL.md +10 -0
  54. package/plugins/lisa/skills/monitor/SKILL.md +4 -5
  55. package/plugins/lisa/skills/notion-access/SKILL.md +193 -0
  56. package/plugins/lisa/skills/notion-prd-intake/SKILL.md +105 -46
  57. package/plugins/lisa/skills/notion-to-tracker/SKILL.md +7 -5
  58. package/plugins/lisa/skills/plan/SKILL.md +4 -5
  59. package/plugins/lisa/skills/research/SKILL.md +4 -5
  60. package/plugins/lisa/skills/setup-atlassian/SKILL.md +316 -0
  61. package/plugins/lisa/skills/setup-confluence/SKILL.md +245 -0
  62. package/plugins/lisa/skills/setup-jira/SKILL.md +198 -0
  63. package/plugins/lisa/skills/setup-notion/SKILL.md +283 -0
  64. package/plugins/lisa/skills/task-decomposition/SKILL.md +2 -0
  65. package/plugins/lisa/skills/ticket-triage/SKILL.md +4 -1
  66. package/plugins/lisa/skills/tracker-evidence/SKILL.md +1 -0
  67. package/plugins/lisa/skills/verification-lifecycle/SKILL.md +2 -0
  68. package/plugins/lisa/skills/verify/SKILL.md +4 -5
  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/skills/ops-browser-uat/SKILL.md +1 -1
  74. package/plugins/lisa-expo/skills/ops-check-logs/SKILL.md +1 -1
  75. package/plugins/lisa-harper-fabric/.claude-plugin/plugin.json +1 -1
  76. package/plugins/lisa-harper-fabric/.codex-plugin/plugin.json +1 -1
  77. package/plugins/lisa-nestjs/.claude-plugin/plugin.json +1 -1
  78. package/plugins/lisa-nestjs/.codex-plugin/plugin.json +1 -1
  79. package/plugins/lisa-nestjs/skills/nestjs-graphql/references/project-patterns.md +48 -0
  80. package/plugins/lisa-rails/.claude-plugin/plugin.json +1 -1
  81. package/plugins/lisa-rails/.codex-plugin/plugin.json +1 -1
  82. package/plugins/lisa-typescript/.claude-plugin/plugin.json +1 -1
  83. package/plugins/lisa-typescript/.codex-plugin/plugin.json +1 -1
  84. package/plugins/src/base/agents/confluence-prd-intake.md +11 -9
  85. package/plugins/src/base/agents/github-agent.md +18 -10
  86. package/plugins/src/base/agents/github-build-intake.md +10 -8
  87. package/plugins/src/base/agents/github-prd-intake.md +11 -9
  88. package/plugins/src/base/agents/jira-agent.md +12 -8
  89. package/plugins/src/base/agents/jira-build-intake.md +9 -7
  90. package/plugins/src/base/agents/learnings-synthesizer.md +1 -1
  91. package/plugins/src/base/agents/linear-agent.md +15 -9
  92. package/plugins/src/base/agents/linear-build-intake.md +13 -11
  93. package/plugins/src/base/agents/linear-prd-intake.md +11 -9
  94. package/plugins/src/base/agents/notion-prd-intake.md +11 -9
  95. package/plugins/src/base/agents/pr-mining-specialist.md +1 -1
  96. package/plugins/src/base/agents/tracker-mining-specialist.md +1 -1
  97. package/plugins/src/base/commands/setup/atlassian.md +7 -0
  98. package/plugins/src/base/commands/setup/confluence.md +7 -0
  99. package/plugins/src/base/commands/setup/jira.md +7 -0
  100. package/plugins/src/base/commands/setup/notion.md +7 -0
  101. package/plugins/src/base/hooks/enforce-team-first.sh +14 -6
  102. package/plugins/src/base/rules/base-rules.md +3 -3
  103. package/plugins/src/base/rules/config-resolution.md +242 -24
  104. package/plugins/src/base/rules/intent-routing.md +4 -4
  105. package/plugins/src/base/rules/repo-scope-split.md +41 -0
  106. package/plugins/src/base/rules/verification.md +13 -0
  107. package/plugins/src/base/skills/atlassian-access/SKILL.md +260 -0
  108. package/plugins/src/base/skills/confluence-prd-intake/SKILL.md +167 -82
  109. package/plugins/src/base/skills/confluence-to-tracker/SKILL.md +39 -26
  110. package/plugins/src/base/skills/debrief/SKILL.md +4 -5
  111. package/plugins/src/base/skills/github-add-journey/SKILL.md +1 -0
  112. package/plugins/src/base/skills/github-build-intake/SKILL.md +104 -40
  113. package/plugins/src/base/skills/github-evidence/SKILL.md +22 -5
  114. package/plugins/src/base/skills/github-prd-intake/SKILL.md +87 -51
  115. package/plugins/src/base/skills/github-to-tracker/SKILL.md +2 -2
  116. package/plugins/src/base/skills/github-validate-issue/SKILL.md +11 -1
  117. package/plugins/src/base/skills/implement/SKILL.md +5 -6
  118. package/plugins/src/base/skills/intake/SKILL.md +5 -6
  119. package/plugins/src/base/skills/jira-add-journey/SKILL.md +1 -0
  120. package/plugins/src/base/skills/jira-build-intake/SKILL.md +110 -45
  121. package/plugins/src/base/skills/jira-create/SKILL.md +5 -3
  122. package/plugins/src/base/skills/jira-evidence/SKILL.md +19 -2
  123. package/plugins/src/base/skills/jira-journey/SKILL.md +3 -1
  124. package/plugins/src/base/skills/jira-read-ticket/SKILL.md +10 -8
  125. package/plugins/src/base/skills/jira-sync/SKILL.md +11 -5
  126. package/plugins/src/base/skills/jira-validate-ticket/SKILL.md +22 -10
  127. package/plugins/src/base/skills/jira-verify/SKILL.md +5 -3
  128. package/plugins/src/base/skills/jira-write-ticket/SKILL.md +16 -14
  129. package/plugins/src/base/skills/linear-add-journey/SKILL.md +1 -0
  130. package/plugins/src/base/skills/linear-build-intake/SKILL.md +90 -32
  131. package/plugins/src/base/skills/linear-evidence/SKILL.md +22 -5
  132. package/plugins/src/base/skills/linear-prd-intake/SKILL.md +92 -57
  133. package/plugins/src/base/skills/linear-validate-issue/SKILL.md +10 -0
  134. package/plugins/src/base/skills/monitor/SKILL.md +4 -5
  135. package/plugins/src/base/skills/notion-access/SKILL.md +193 -0
  136. package/plugins/src/base/skills/notion-prd-intake/SKILL.md +105 -46
  137. package/plugins/src/base/skills/notion-to-tracker/SKILL.md +7 -5
  138. package/plugins/src/base/skills/plan/SKILL.md +4 -5
  139. package/plugins/src/base/skills/research/SKILL.md +4 -5
  140. package/plugins/src/base/skills/setup-atlassian/SKILL.md +316 -0
  141. package/plugins/src/base/skills/setup-confluence/SKILL.md +245 -0
  142. package/plugins/src/base/skills/setup-jira/SKILL.md +198 -0
  143. package/plugins/src/base/skills/setup-notion/SKILL.md +283 -0
  144. package/plugins/src/base/skills/task-decomposition/SKILL.md +2 -0
  145. package/plugins/src/base/skills/ticket-triage/SKILL.md +4 -1
  146. package/plugins/src/base/skills/tracker-evidence/SKILL.md +1 -0
  147. package/plugins/src/base/skills/verification-lifecycle/SKILL.md +2 -0
  148. package/plugins/src/base/skills/verify/SKILL.md +4 -5
  149. package/plugins/src/expo/skills/ops-browser-uat/SKILL.md +1 -1
  150. package/plugins/src/expo/skills/ops-check-logs/SKILL.md +1 -1
  151. package/plugins/src/nestjs/skills/nestjs-graphql/references/project-patterns.md +48 -0
  152. package/scripts/check-plugins-sync.sh +45 -0
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: linear-prd-intake
3
- description: "Scans a Linear workspace (or a specific team) for projects labelled `prd-ready` and runs each one through the dry-run validation pipeline. Projects that pass every gate get tickets written and the label flipped to `prd-ticketed`; projects that fail get clarifying-question comments (on a sentinel feedback issue under the project) and the label flipped to `prd-blocked`. 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)."
3
+ description: "Scans a Linear workspace (or a specific team) for projects carrying the configured `ready` PRD label and runs each one through the dry-run validation pipeline. Projects that pass every gate get tickets written and the label flipped to the configured `ticketed` label; projects that fail get 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
 
@@ -8,52 +8,78 @@ allowed-tools: ["Skill", "Bash", "mcp__linear-server__list_projects", "mcp__line
8
8
 
9
9
  `$ARGUMENTS` is one of:
10
10
 
11
- - A Linear **workspace** URL — scans every project in the workspace whose labels include `prd-ready`. Example: `https://linear.app/acme`.
12
- - A Linear **team** URL or team key — scans every project on the team whose labels include `prd-ready`. Example: `https://linear.app/acme/team/ENG/projects` or bare `ENG`.
11
+ - A Linear **workspace** URL — scans every project in the workspace whose labels include the configured `ready` label. Example: `https://linear.app/acme`.
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. Each project with the `prd-ready` label is claimed, validated, and routed to either `prd-blocked` (with clarifying comments on a sentinel feedback issue) or `prd-ticketed` (with JIRA tickets created).
15
+ Run one intake cycle against that scope. Each project with the `ready` label is claimed, validated, and routed to either the `blocked` label (with clarifying comments on a sentinel feedback issue) or the `ticketed` label (with destination tickets created).
16
16
 
17
- This skill is the Linear counterpart of `lisa:notion-prd-intake` and `lisa:confluence-prd-intake`. The phases, gates, comment templates, and rules are identical — the only differences are (1) the lifecycle is encoded as **project labels** instead of a Status property, (2) the fetch / update tools are Linear MCP, and (3) clarifying-question comments land on a sentinel feedback Issue under the project (because Linear's MCP does not expose project-level comments). Keep all three skills behaviorally aligned: when changing intake logic, change them together.
17
+ ## Workflow resolution
18
+
19
+ PRD label names are read from `.lisa.config.json` `linear.labels.prd.*`, falling back to defaults documented in the `config-resolution` rule. Bash pattern:
20
+
21
+ ```bash
22
+ # Read role with default fallback. Local overrides global per-key.
23
+ read_role() {
24
+ local role="$1" default="$2"
25
+ local local_v global_v
26
+ local_v=$(jq -r ".linear.labels.prd.${role} // empty" .lisa.config.local.json 2>/dev/null)
27
+ global_v=$(jq -r ".linear.labels.prd.${role} // empty" .lisa.config.json 2>/dev/null)
28
+ echo "${local_v:-${global_v:-$default}}"
29
+ }
30
+
31
+ READY=$(read_role ready "prd-ready")
32
+ IN_REVIEW=$(read_role in_review "prd-in-review")
33
+ BLOCKED=$(read_role blocked "prd-blocked")
34
+ TICKETED=$(read_role ticketed "prd-ticketed")
35
+ SHIPPED=$(read_role shipped "prd-shipped")
36
+ SENTINEL=$(read_role sentinel "prd-intake-feedback")
37
+ ```
38
+
39
+ In prose below, the role names refer to the resolved labels: e.g. "the `ready` label" means whatever `linear.labels.prd.ready` resolves to (default: `prd-ready`).
40
+
41
+ This skill is the Linear counterpart of `lisa:notion-prd-intake` and `lisa:confluence-prd-intake`. The phases, gates, comment templates, and rules are identical — the only differences are (1) the lifecycle is encoded as **project labels** instead of a status property, (2) the fetch / update tools are Linear MCP, and (3) clarifying-question comments land on a sentinel feedback Issue under the project (because Linear's MCP does not expose project-level comments). Keep all three skills behaviorally aligned: when changing intake logic, change them together.
18
42
 
19
43
  ## Confirmation policy
20
44
 
21
- Do NOT ask the caller whether to proceed. Once invoked with a workspace/team scope, run the cycle to completion — claim, validate, branch to `prd-blocked` or `prd-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.
45
+ 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 batch.
22
46
 
23
47
  Specifically forbidden:
24
48
 
25
49
  - Previewing projected scope (epic count, story count, write count) and asking whether to continue.
26
50
  - Offering A/B/C-style choices like "proceed / skip / dry-run only" — the documented behavior IS the default.
27
- - Pausing because a PRD looks large, has many open questions, or is likely to end in `prd-blocked`. `prd-blocked` is a valid terminal state of this lifecycle, not a failure mode — routing a PRD to `prd-blocked` with gate-failure comments is exactly how this skill communicates "the PRD needs more work before it can be ticketed." That outcome is success.
51
+ - Pausing because a PRD looks large, has many open questions, or is likely to end in `$BLOCKED`. The `blocked` label is a valid terminal state of this lifecycle, not a failure mode — routing a PRD there with gate-failure comments is exactly how this skill communicates "the PRD needs more work before it can be ticketed." That outcome is success.
28
52
  - Pausing because the dry-run validation looks expensive. The cost of one cycle is bounded; the cost of stalling a scheduled cron waiting on a human is unbounded.
29
53
 
30
54
  The only legitimate reasons to stop early:
31
55
 
32
56
  - Missing scope argument or required configuration (`linear.workspace` in `.lisa.config.json`, `E2E_BASE_URL`, etc.). Surface the missing key(s) and exit this cycle — never invent values.
33
- - Workspace/team unreachable, or the labelling convention not yet adopted (no projects carry any of `prd-ready` / `prd-in-review` / `prd-blocked` / `prd-ticketed`). Surface and exit.
34
- - Empty `prd-ready` set. Exit cleanly with `"No Linear projects labelled prd-ready. Nothing to do."`
57
+ - Workspace/team unreachable, or the labelling convention not yet adopted (no projects carry any of `$READY` / `$IN_REVIEW` / `$BLOCKED` / `$TICKETED`). Surface and exit.
58
+ - Empty ready set. Exit cleanly with `"No Linear projects labelled $READY. Nothing to do."`
35
59
 
36
60
  ## Lifecycle assumed
37
61
 
38
62
  The Linear PRD lifecycle is encoded as **project labels** (we deliberately do NOT key off Linear's native project state, since project state is product's day-to-day signal and we don't want to fight it). Exactly one of these labels is expected on a project at any time:
39
63
 
40
64
  ```text
41
- prd-draft → prd-ready → prd-in-reviewprd-blocked | prd-ticketed → prd-shipped
42
- (product) (us) (us) (product)
65
+ draft → ready → in_review → blocked | ticketed → shipped
66
+ (product) (us) (us) (product)
43
67
  ```
44
68
 
69
+ (Defaults: `prd-draft` / `prd-ready` / `prd-in-review` / `prd-blocked` / `prd-ticketed` / `prd-shipped`.)
70
+
45
71
  This skill ONLY transitions:
46
72
 
47
- - `prd-ready` → `prd-in-review` (claim)
48
- - `prd-in-review` → `prd-blocked` (gate failures or coverage gaps)
49
- - `prd-in-review` → `prd-ticketed` (success)
50
- - `prd-ticketed` → `prd-blocked` (post-write coverage gaps from Phase 3e)
73
+ - `$READY` → `$IN_REVIEW` (claim)
74
+ - `$IN_REVIEW` → `$BLOCKED` (gate failures or coverage gaps)
75
+ - `$IN_REVIEW` → `$TICKETED` (success)
76
+ - `$TICKETED` → `$BLOCKED` (post-write coverage gaps from Phase 3e)
51
77
 
52
- It never adds, removes, or touches `prd-draft` or `prd-shipped`. Those labels are owned by product.
78
+ It never adds, removes, or touches the `draft` or `shipped` labels. Those labels are owned by product.
53
79
 
54
80
  A "transition" means: remove the old lifecycle label and add the new one in a single `save_project` call (passing the full new label set in the `labels` array). The skill MUST verify that exactly one lifecycle label exists on the project after the update — having two simultaneously breaks idempotency.
55
81
 
56
- If the project does not yet use `prd-*` labels, this skill cannot run. Adopting the convention is a one-time setup the project owner does (see "Adoption" at the bottom of this file).
82
+ If the project does not yet use these labels, this skill cannot run. Adopting the convention is a one-time setup the project owner does (see "Adoption" at the bottom of this file).
57
83
 
58
84
  ## Phases
59
85
 
@@ -67,32 +93,32 @@ If the project does not yet use `prd-*` labels, this skill cannot run. Adopting
67
93
  2. Verify the scope is reachable:
68
94
  - For a workspace: call `mcp__linear-server__list_teams` and confirm at least one team is returned (non-empty workspaces are readable; empty results indicate auth or workspace-mismatch).
69
95
  - For a team: call `mcp__linear-server__list_teams({query: <KEY>})` and confirm the team resolves.
70
- 3. Resolve the project-label IDs for `prd-ready`, `prd-in-review`, `prd-blocked`, `prd-ticketed` via `mcp__linear-server__list_project_labels`. Cache them — every transition uses these IDs. If any of the four are missing, surface a label-convention error and exit (see "Adoption").
96
+ 3. Resolve the project-label IDs for `$READY`, `$IN_REVIEW`, `$BLOCKED`, `$TICKETED` via `mcp__linear-server__list_project_labels`. Cache them — every transition uses these IDs. If any of the four are missing, surface a label-convention error and exit (see "Adoption").
71
97
 
72
- ### Phase 2 — Find Ready PRDs
98
+ ### Phase 2 — Find ready PRDs
73
99
 
74
- Call `mcp__linear-server__list_projects({label: "prd-ready", ...scope-filter})`:
100
+ Call `mcp__linear-server__list_projects({label: "$READY", ...scope-filter})`:
75
101
 
76
- - For a workspace scope: pass `label: "prd-ready"` only.
77
- - For a team scope: pass `label: "prd-ready"` AND `team: "<KEY>"`.
102
+ - For a workspace scope: pass `label: "$READY"` only.
103
+ - For a team scope: pass `label: "$READY"` AND `team: "<KEY>"`.
78
104
 
79
- The query returns the list of candidate projects with IDs, names, and label sets. For each candidate, confirm that exactly one lifecycle label is present (the API filter is `prd-ready`, but a project could have ended up with two labels by hand — that's a misconfiguration, not a normal queue entry).
105
+ The query returns the list of candidate projects with IDs, names, and label sets. For each candidate, confirm that exactly one lifecycle label is present (the API filter is `$READY`, but a project could have ended up with two labels by hand — that's a misconfiguration, not a normal queue entry).
80
106
 
81
107
  If the result set is empty, run a secondary query to distinguish between a genuinely empty queue and a workspace/team that has not yet adopted the label convention:
82
108
 
83
- - Secondary query: `list_projects({...scope-filter})` with no label filter, then in-process check whether any returned project carries any of `prd-ready` / `prd-in-review` / `prd-blocked` / `prd-ticketed`.
109
+ - Secondary query: `list_projects({...scope-filter})` with no label filter, then in-process check whether any returned project carries any of `$READY` / `$IN_REVIEW` / `$BLOCKED` / `$TICKETED`.
84
110
 
85
- If the secondary query shows zero projects carrying any `prd-*` label → the convention has not been adopted. Surface a misconfiguration message: `"No Linear projects in this scope carry prd-* labels. If this is a new project, apply the prd-ready label to projects that are ready for ticketing (see Adoption section)."` Exit with an error — this is a setup issue, not a normal idle cycle.
111
+ If the secondary query shows zero projects carrying any PRD lifecycle label → the convention has not been adopted. Surface a misconfiguration message: `"No Linear projects in this scope carry PRD lifecycle labels. If this is a new project, apply the $READY label to projects that are ready for ticketing (see Adoption section)."` Exit with an error — this is a setup issue, not a normal idle cycle.
86
112
 
87
- If the secondary query shows projects with other `prd-*` labels but none with `prd-ready` → the queue is genuinely empty (all PRDs are already in-review, blocked, ticketed, or shipped). Exit cleanly with `"No Linear projects labelled prd-ready. Nothing to do."`
113
+ 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."`
88
114
 
89
- ### Phase 3 — Process each Ready PRD
115
+ ### Phase 3 — Process each ready PRD
90
116
 
91
117
  For each project (process serially to keep label transitions auditable):
92
118
 
93
119
  #### 3a. Claim
94
120
 
95
- Transition labels via `mcp__linear-server__save_project({id, labels})`: pass the full new label set with `prd-ready` removed and `prd-in-review` added. This is the idempotency lock — a re-entrant cycle running concurrently won't see this project because its query filters on `label: "prd-ready"`.
121
+ Transition labels via `mcp__linear-server__save_project({id, labels})`: pass the full new label set with `$READY` removed and `$IN_REVIEW` added. This is the idempotency lock — a re-entrant cycle running concurrently won't see this project because its query filters on `label: "$READY"`.
96
122
 
97
123
  If the update fails (permission error, race condition), log it and skip this project. Do not proceed to validation on a project you didn't successfully claim.
98
124
 
@@ -114,8 +140,8 @@ This call also indirectly invokes `lisa:tracker-source-artifacts` (artifact extr
114
140
 
115
141
  1. Re-invoke `lisa:linear-to-tracker` with `dry_run: false` to actually write the tickets. This re-runs Phases 1-5 and runs the preservation gate (Phase 5.5).
116
142
  2. Capture the created ticket keys from the skill's output.
117
- 3. Ensure the project has a sentinel feedback issue (see "Sentinel feedback issue" below for the helper). Post a comment on it via `mcp__linear-server__save_comment` listing the created tickets (epic, stories, sub-tasks) with their JIRA URLs. Lead with: `"Ticketed by Claude. Created N JIRA issues — see below. Add the prd-shipped label to the Linear project after the work is delivered."`
118
- 4. Transition labels: remove `prd-in-review`, add `prd-ticketed` via `save_project`.
143
+ 3. Ensure the project has a sentinel feedback issue (see "Sentinel feedback issue" below for the helper). Post a comment on it via `mcp__linear-server__save_comment` listing the created tickets (epic, stories, sub-tasks) with their JIRA URLs. Lead with: `"Ticketed by Claude. Created N JIRA issues — see below. Add the $SHIPPED label to the Linear project after the work is delivered."`
144
+ 4. Transition labels: remove `$IN_REVIEW`, add `$TICKETED` via `save_project`.
119
145
  5. **Run Phase 3e (coverage audit)** before considering this PRD done.
120
146
 
121
147
  **If `FAIL`** (one or more planned tickets failed one or more gates):
@@ -144,7 +170,7 @@ Each comment body MUST contain these four parts, in this order, no exceptions:
144
170
 
145
171
  **Recommendation:** <validator's `recommendation` field, verbatim — must contain 1–3 concrete options, never a generic "please clarify">
146
172
 
147
- **Action:** Update this section in the PRD, then replace the `prd-blocked` label with `prd-ready` on the Linear project and Claude will re-run intake.
173
+ **Action:** Update this section in the PRD, then replace the `$BLOCKED` label with `$READY` on the Linear project and Claude will re-run intake.
148
174
  ```
149
175
 
150
176
  If multiple failures share an anchor, render each as its own `**What's unclear:** ... **Recommendation:** ...` block within the same comment, separated by horizontal lines (`---`). Keep the single `[Category badge]` heading at the top using the most-severe / most-blocking category from the group.
@@ -175,13 +201,13 @@ Use these exact badge labels — they are the validator's category values transl
175
201
 
176
202
  ##### 3c.6 Label transition
177
203
 
178
- After all comments are posted (anchored groups + the optional sentinel-issue summary), transition labels: remove `prd-in-review`, add `prd-blocked` via `save_project`. Do NOT write any JIRA tickets.
204
+ 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.
179
205
 
180
206
  #### 3d. Continue
181
207
 
182
- Move to the next Ready PRD. One PRD failing does not affect others.
208
+ Move to the next ready PRD. One PRD failing does not affect others.
183
209
 
184
- #### 3e. Coverage audit (mandatory after prd-ticketed)
210
+ #### 3e. Coverage audit (mandatory after $TICKETED)
185
211
 
186
212
  Per-ticket gates prove each ticket is well-formed; they do NOT prove the *set* of created tickets covers the *whole* PRD. Silent drops happen — invoke the `lisa:prd-ticket-coverage` skill to catch them.
187
213
 
@@ -190,16 +216,16 @@ Per-ticket gates prove each ticket is well-formed; they do NOT prove the *set* o
190
216
 
191
217
  | Verdict | Action |
192
218
  |---------|--------|
193
- | `COMPLETE` | Done. Leave label as `prd-ticketed`. Move to next PRD. |
194
- | `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 `prd-ticketed`. |
195
- | `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 `prd-ticketed` back to `prd-blocked` via `save_project`. |
196
- | `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 `prd-ticketed` with a comment flagging the audit failure for human review. |
219
+ | `COMPLETE` | Done. Leave label as `$TICKETED`. Move to next PRD. |
220
+ | `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`. |
221
+ | `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`. |
222
+ | `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. |
197
223
 
198
224
  3. The created tickets remain in the destination tracker regardless of the verdict — they are valid in their own right. The audit only tells us whether *more* are needed.
199
225
 
200
226
  ### Phase 4 — Summary report
201
227
 
202
- After processing every Ready PRD, emit a summary:
228
+ After processing every ready PRD, emit a summary:
203
229
 
204
230
  ```text
205
231
  ## linear-prd-intake summary
@@ -209,18 +235,18 @@ Cycle started: <ISO timestamp>
209
235
  Cycle completed: <ISO timestamp>
210
236
 
211
237
  PRDs processed: <n>
212
- - prd-ticketed: <n>
238
+ - $TICKETED: <n>
213
239
  - <project name> → <epic-key> + <story-count> stories + <subtask-count> sub-tasks (coverage: COMPLETE | COMPLETE_WITH_SCOPE_CREEP)
214
- - prd-blocked: <n>
240
+ - $BLOCKED: <n>
215
241
  - <project name> → <gate-failure-count> gate failures (pre-write) OR <gap-count> coverage gaps (post-write)
216
242
  - Errors (claim failed, etc): <n>
217
243
  - <project name> — <reason>
218
244
 
219
- Total JIRA tickets created: <n>
245
+ Total destination tickets created: <n>
220
246
  Coverage audit summary: <n> COMPLETE / <n> COMPLETE_WITH_SCOPE_CREEP / <n> GAPS_FOUND
221
247
  ```
222
248
 
223
- Print to the agent's output. Do not write this summary to Linear or JIRA — it's an operational record for the human.
249
+ Print to the agent's output. Do not write this summary to Linear or the destination tracker — it's an operational record for the human.
224
250
 
225
251
  ## Sentinel feedback issue
226
252
 
@@ -229,52 +255,61 @@ Linear's MCP does not expose project-level comments. To preserve the comment-bas
229
255
  The sentinel issue is identified by:
230
256
 
231
257
  - A stable title: `"PRD intake: clarifying questions"`
232
- - A stable label: `prd-intake-feedback` (issue-level label, distinct from the project-level `prd-*` labels)
258
+ - A stable label: `$SENTINEL` (issue-level label, distinct from the project-level PRD lifecycle labels)
233
259
  - Membership in the project being processed
234
260
 
235
261
  Helper behavior — call this **before** posting any clarifying-question comment in Phase 3c or 3e:
236
262
 
237
- 1. Search for an existing feedback issue: `list_issues({project: <id>, label: "prd-intake-feedback"})`. If multiple match (shouldn't happen, but defensive), use the oldest by `createdAt`.
238
- 2. If none exists: ensure the `prd-intake-feedback` label exists on the project's team via `list_issue_labels` then `create_issue_label` if needed; then create the sentinel via `save_issue({team: <team-id>, project: <id>, title: "PRD intake: clarifying questions", description: "Auto-created by lisa:linear-prd-intake. This issue collects clarifying-question comments that don't anchor to a specific sub-issue. Do not close manually — it is reused across intake cycles.", labels: ["prd-intake-feedback"]})`. Capture the new issue identifier.
263
+ 1. Search for an existing feedback issue: `list_issues({project: <id>, label: "$SENTINEL"})`. If multiple match (shouldn't happen, but defensive), use the oldest by `createdAt`.
264
+ 2. If none exists: ensure the `$SENTINEL` label exists on the project's team via `list_issue_labels` then `create_issue_label` if needed; then create the sentinel via `save_issue({team: <team-id>, project: <id>, title: "PRD intake: clarifying questions", description: "Auto-created by lisa:linear-prd-intake. This issue collects clarifying-question comments that don't anchor to a specific sub-issue. Do not close manually — it is reused across intake cycles.", labels: ["$SENTINEL"]})`. Capture the new issue identifier.
239
265
  3. Return the issue identifier to the caller for use in `save_comment({issueId: <id>, body: ...})`.
240
266
 
241
267
  Idempotency: the helper finds-or-creates. Re-runs of the cycle reuse the same sentinel issue. Comments accumulate; product reads top-down to see the latest cycle's findings. Do not delete or repurpose old comments — history is the audit trail.
242
268
 
243
269
  ## Idempotency & safety
244
270
 
245
- - **Single-cycle scope**: this skill processes the `prd-ready` set as it exists at the start of Phase 2. New `prd-ready` projects added mid-cycle are picked up next run.
246
- - **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 `prd-in-review`, `prd-blocked`, `prd-ticketed`, 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 `prd-draft` or `prd-shipped`, never archives or deletes projects.
247
- - **Claim-first ordering**: the label flip to `prd-in-review` happens BEFORE validation runs, so a re-entrant call won't double-process.
248
- - **Failure isolation**: an exception processing one project must not stop the cycle. Catch, record under "Errors" in the summary, continue to the next project. The project that errored is left labelled `prd-in-review` — the human investigates from there.
271
+ - **Single-cycle scope**: this skill processes the ready set as it exists at the start of Phase 2. New `$READY` projects added mid-cycle are picked up next run.
272
+ - **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`, 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` or `shipped` labels, never archives or deletes projects.
273
+ - **Claim-first ordering**: the label flip to `$IN_REVIEW` happens BEFORE validation runs, so a re-entrant call won't double-process.
274
+ - **Failure isolation**: an exception processing one project must not stop the cycle. Catch, record under "Errors" in the summary, continue to the next project. The project that errored is left labelled `$IN_REVIEW` — the human investigates from there.
249
275
  - **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.
250
276
 
251
277
  ## Configuration
252
278
 
253
279
  Same configuration as `lisa:linear-to-tracker`. See that skill for the full table. Key items:
254
280
 
255
- - **From `.lisa.config.json`**: `linear.workspace` (required for Linear MCP). When the destination tracker is `linear`, also `linear.teamKey`.
281
+ - **From `.lisa.config.json`**: `linear.workspace` (required for Linear MCP). When the destination tracker is `linear`, also `linear.teamKey`. Lifecycle label vocabulary lives under `linear.labels.prd.*` (all optional; defaults documented above).
256
282
  - **From environment variables**: `E2E_BASE_URL`, `E2E_TEST_PHONE`, `E2E_TEST_OTP`, `E2E_TEST_ORG`, `E2E_GRAPHQL_URL` (operational E2E test config).
257
283
 
258
284
  Destination tracker config (jira / github / linear) is consumed by `lisa:tracker-write` internally — this skill does NOT read it. If any required value is missing, surface the missing key(s) and exit this cycle — never invent values.
259
285
 
286
+ | Field | Default | Purpose |
287
+ |-------|---------|---------|
288
+ | `.lisa.config.json` `linear.labels.prd.ready` | `prd-ready` | Project label signalling "PRD ready for ticketing" |
289
+ | `.lisa.config.json` `linear.labels.prd.in_review` | `prd-in-review` | Project label set on claim |
290
+ | `.lisa.config.json` `linear.labels.prd.blocked` | `prd-blocked` | Project label set on validation failure |
291
+ | `.lisa.config.json` `linear.labels.prd.ticketed` | `prd-ticketed` | Project label set on success |
292
+ | `.lisa.config.json` `linear.labels.prd.shipped` | `prd-shipped` | Project label product sets after delivery |
293
+ | `.lisa.config.json` `linear.labels.prd.sentinel` | `prd-intake-feedback` | Issue-level label marking the sentinel feedback issue |
294
+
260
295
  ## Rules
261
296
 
262
297
  - Never write to the destination tracker outside of `lisa:linear-to-tracker` → `lisa:tracker-write`. The validator's verdict gates progress; bypassing it produces broken tickets.
263
- - Never add or remove a label this skill doesn't own (`prd-in-review`, `prd-blocked`, `prd-ticketed`). Product owns `prd-draft`, `prd-ready`, `prd-shipped`. The issue-level `prd-intake-feedback` label is owned by this skill but is not a lifecycle label.
298
+ - Never add or remove a label this skill doesn't own (`$IN_REVIEW`, `$BLOCKED`, `$TICKETED`). Product owns the `draft`, `ready`, and `shipped` labels. The issue-level `$SENTINEL` label is owned by this skill but is not a lifecycle label.
264
299
  - Never edit a project's description or any attached Linear document. Communication with product happens only through comments on sub-issues or on the sentinel feedback issue.
265
300
  - Never post a single dump of all gate failures on one comment. One comment per `prd_anchor` group on the relevant sub-issue (or one comment on the sentinel feedback issue for unanchored failures only). Comments must be sub-issue-anchored where possible, categorized, plain-language, and contain a concrete recommendation.
266
301
  - Never include a gate ID, internal skill name, or engineering shorthand in a comment body.
267
302
  - Never run more than one intake cycle concurrently against the same scope. This skill assumes serial execution.
268
303
  - Never close, archive, or otherwise modify the sentinel feedback issue except to post comments on it. Its longevity is the audit trail.
269
- - If `lisa:linear-to-tracker` returns errors, treat them as gate failures: comment + `prd-blocked`. Don't silently fail.
304
+ - If `lisa:linear-to-tracker` returns errors, treat them as gate failures: comment + `$BLOCKED`. Don't silently fail.
270
305
 
271
306
  ## Adoption (one-time per project)
272
307
 
273
- Before this skill can run against a Linear workspace or team, the team must adopt the `prd-*` project-label convention:
308
+ Before this skill can run against a Linear workspace or team, the team must adopt the PRD lifecycle project-label convention (defaults shown; override via `linear.labels.prd.*` if you want different names):
274
309
 
275
- 1. Apply `prd-ready` to projects that are ready for ticketing (replaces the Notion `Status = Ready` flip and the Confluence `prd-ready` page label).
276
- 2. Reserve `prd-in-review`, `prd-blocked`, `prd-ticketed` for this skill — humans should not set them manually except to recover from an error.
277
- 3. (Optional but recommended) Add `prd-draft` for in-progress PRDs and `prd-shipped` for delivered work, so the full lifecycle is visible at a glance.
310
+ 1. Apply the `ready` label (default: `prd-ready`) to projects that are ready for ticketing (replaces the Notion `Status = Ready` flip and the Confluence `prd-ready` page label).
311
+ 2. Reserve `in_review`, `blocked`, `ticketed` (defaults: `prd-in-review`, `prd-blocked`, `prd-ticketed`) for this skill — humans should not set them manually except to recover from an error.
312
+ 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.
278
313
  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.
279
314
 
280
315
  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 the queue is drained. See Phase 2 for how the skill detects this case.
@@ -86,6 +86,7 @@ Each gate is tagged with a fixed `category` and a `product_relevant` boolean. Ca
86
86
  | S11 Validation Journey | `acceptance-criteria` | true |
87
87
  | S12 Source Precedence | `design-ux` | true |
88
88
  | S13 Relationship Search | `dependency` | true |
89
+ | S14 Evidence manifest binding (leaf work units) | `acceptance-criteria` | true |
89
90
  | F1 Issue type valid in team | `structural` | false |
90
91
  | F2 Project parent exists and is in same team | `structural` | false |
91
92
  | F3 Linked items exist | `structural` | false |
@@ -191,6 +192,14 @@ The item must EITHER have at least one entry in `relations`, OR the description
191
192
 
192
193
  An item with zero relations and no documented search: FAIL.
193
194
 
195
+ #### S14 — Evidence manifest binding (leaf work units)
196
+
197
+ When `issue_type ∈ {Bug, Task, Sub-task, Improvement}` AND `runtime_behavior_change = true`, the `## Validation Journey` must declare at least one `[EVIDENCE: name]` marker. Each marker name must be kebab-case and unique within the item. These markers are the work unit's **evidence manifest** — the exact, enumerated set of artifacts that must be captured and attached before the item may be closed (see the "Per-Work-Unit Evidence Contract" section of the `verification` rule, the Definition of Done in `verification-lifecycle`, and the evidence-manifest gate in `tracker-evidence`).
198
+
199
+ FAIL when the Validation Journey is present but declares zero `[EVIDENCE: name]` markers, or when any marker name is empty, duplicated, or not kebab-case. A behavior-changing work unit SHOULD declare both a success marker and an error/edge marker; a journey with only one marker passes but the remediation should recommend adding the error/edge case.
200
+
201
+ This gate depends on S11. It is `N/A` for Project / Story / Spike (coordination containers, not work units) and for leaf units with `runtime_behavior_change = false` (doc-only / config-only / type-only). If S11 fails because the Validation Journey is absent, S14 also FAILs (there is no manifest to bind) with remediation pointing back to `lisa:linear-add-journey`.
202
+
194
203
  ### Feasibility Gates (require Linear lookups; skip in dry-run if requested)
195
204
 
196
205
  #### F1 — Issue type valid in team
@@ -242,6 +251,7 @@ Output is a single fenced text block. Callers parse it; do not add free-form pro
242
251
  - [PASS|FAIL|N/A] S11 Validation Journey — <one-line reason>
243
252
  - [PASS|FAIL|N/A] S12 Source Precedence — <one-line reason>
244
253
  - [PASS|FAIL|N/A] S13 Relationship Search — <one-line reason>
254
+ - [PASS|FAIL|N/A] S14 Evidence manifest binding — <one-line reason>
245
255
 
246
256
  ### Feasibility Gates (omit when --spec-only)
247
257
  - [PASS|FAIL|N/A] F1 Issue type valid in team — <one-line reason>
@@ -10,14 +10,13 @@ Spot-check application health in the named environment (`dev` / `staging` / `pro
10
10
 
11
11
  ## Orchestration: agent team
12
12
 
13
- If you are NOT already operating inside an agent team (no prior `TeamCreate` in this session, not spawned via `Agent` with `team_name`), the very first thing you do is create the team. Two tool calls only, in this exact order:
13
+ If you are NOT already operating inside an agent team (no prior successful team-creation tool call in this session, not spawned into a team context), the very first thing you do is establish team orchestration.
14
14
 
15
- 1. `ToolSearch` with `query: "select:TeamCreate"` `TeamCreate` is a deferred tool whose schema must be loaded before it can be invoked. A cold call returns `InputValidationError` and tempts a fallback to direct `Agent` calls, which bypasses the team.
16
- 2. `TeamCreate` — actually create the team.
15
+ Use `TeamCreate` if available. In Claude, if `TeamCreate` has not been loaded yet, first use `ToolSearch` with `query: "select:TeamCreate"` to load its schema. If `TeamCreate` is not available, use the current runtime's tool-discovery mechanism (for Codex, `tool_search`) to discover available multi-agent/team tools, then call the appropriate team creation tool. If no team creation tool is available, explicitly state that team orchestration is unavailable in this runtime, continue as the lead agent, and preserve the workflow's review, verification, and task-tracking obligations locally.
17
16
 
18
- Until `TeamCreate` returns successfully, do NOT call any of: `Agent`, `TaskCreate`, `Skill`, MCP tools (Atlassian / Linear / GitHub / Notion / Sentry), `Read`, `Write`, `Edit`, `Bash`, `Grep`, `Glob`. Hitting health endpoints, pulling logs, querying Sentry — all of those are tasks for the team you are about to create, not for the lead session before the team exists.
17
+ Until the team is established or the no-team fallback has been declared, do NOT call any of: `Agent`, `TaskCreate`, `Skill`, MCP tools (Atlassian / Linear / GitHub / Notion / Sentry), `Read`, `Write`, `Edit`, `Bash`, `Grep`, `Glob`. Hitting health endpoints, pulling logs, querying Sentry — all of those are tasks for the team you are about to create, not for the lead session before orchestration exists.
19
18
 
20
- If you ARE already inside an agent team (e.g., a teammate invoked this skill via the Skill tool), do NOT call `TeamCreate`the harness rejects double-creates. Continue within the existing team. The team lead created the team; teammates inherit it.
19
+ If you ARE already inside an agent team (e.g., a teammate invoked this skill via the Skill tool), do NOT create a second team many harnesses reject double-creates. Continue within the existing team. The team lead created the team; teammates inherit it.
21
20
 
22
21
  ## Flow
23
22
 
@@ -0,0 +1,193 @@
1
+ ---
2
+ name: notion-access
3
+ description: "Vendor-neutral access layer for Notion. Every notion-* skill MUST delegate through this skill rather than invoking the Notion REST API or any Notion MCP directly. Resolves a substrate per operation in this order: (1) Notion MCP if authenticated and the configured prdDatabaseId is fetchable through it (identity-match), (2) curl + Bearer auth + internal-integration token. Verifies the active connection matches `.lisa.config.json` before every operation — substrates authenticated as a different Notion workspace are skipped, not used."
4
+ allowed-tools: ["Bash", "Read", "Skill"]
5
+ ---
6
+
7
+ # Notion Access: $ARGUMENTS
8
+
9
+ Single chokepoint for all Notion operations. Routes each op to a substrate, enforces connection match, returns structured result. Caller skills (`notion-*`) MUST go through this — they MUST NOT call the Notion REST API or any `mcp__*notion*` tools directly.
10
+
11
+ ## Invocation contract
12
+
13
+ ```text
14
+ operation: read-page id: <uuid>
15
+ operation: write-page payload: {...} # update page properties
16
+ operation: archive-page id: <uuid>
17
+ operation: query-database id: <uuid> filter: {...} sort: {...}
18
+ operation: read-database id: <uuid>
19
+ operation: append-blocks page_id: <uuid> children: [...]
20
+ operation: search query: "..." [filter: { object: "page" }]
21
+ operation: list-users
22
+ operation: get-self
23
+ ```
24
+
25
+ The skill returns either the structured operation result (JSON) or an error message prefixed with `Error:` and a remediation hint.
26
+
27
+ ## Workflow
28
+
29
+ ### Step 1 — Substrate selection (per operation)
30
+
31
+ Read config:
32
+
33
+ ```bash
34
+ WORKSPACE=$(jq -r '.notion.workspaceId // empty' .lisa.config.json)
35
+ DB_ID=$(jq -r '.notion.prdDatabaseId // empty' .lisa.config.json)
36
+ [ -z "$WORKSPACE" ] && { echo "Error: notion.workspaceId not set. Run /lisa:setup:notion." >&2; exit 1; }
37
+ [ -z "$DB_ID" ] && { echo "Error: notion.prdDatabaseId not set. Run /lisa:setup:notion." >&2; exit 1; }
38
+ ```
39
+
40
+ Probe each tier in order; the first that's ready AND identity-matches is the substrate for this operation. Identity-match is verified before any operation; substrates authenticated as a different workspace are skipped, not used.
41
+
42
+ ```bash
43
+ substrate=""
44
+
45
+ # Tier 1: Notion MCP (identity-matched by fetching the configured PRD database)
46
+ # Pseudo-code; actual call is the MCP tool invocation.
47
+ # Try to fetch DB_ID through the MCP. Success → MCP is authed to the right workspace.
48
+ # 404 / object_not_found → MCP is authed elsewhere (or unauthenticated). Skip.
49
+ if mcp_notion_can_fetch_database "$DB_ID"; then
50
+ substrate="mcp"
51
+ fi
52
+
53
+ # Tier 2: curl + API token
54
+ read_notion_token() {
55
+ local workspace="$1"
56
+ [ -n "$NOTION_API_TOKEN" ] && { echo "$NOTION_API_TOKEN"; return; }
57
+ local slug=$(echo "$workspace" | tr '[:upper:]-' '[:lower:]_')
58
+ local varname="NOTION_API_TOKEN_${slug}"
59
+ [ -n "${!varname}" ] && { echo "${!varname}"; return; }
60
+ case "$(uname -s)" in
61
+ Darwin) security find-generic-password -s lisa-notion -a "$workspace" -w 2>/dev/null ;;
62
+ Linux) command -v secret-tool >/dev/null && \
63
+ secret-tool lookup service lisa-notion account "$workspace" 2>/dev/null ;;
64
+ MINGW*|MSYS*|CYGWIN*) cmdkey /list:"lisa-notion-${workspace}" 2>/dev/null | grep Password | awk '{print $NF}' ;;
65
+ esac
66
+ }
67
+ TOKEN=$(read_notion_token "$WORKSPACE")
68
+ if [ -n "$TOKEN" ]; then
69
+ # Verify token belongs to the configured workspace.
70
+ me=$(curl -s -H "Authorization: Bearer $TOKEN" -H "Notion-Version: 2022-06-28" \
71
+ "https://api.notion.com/v1/users/me")
72
+ me_workspace=$(echo "$me" | jq -r '.bot.workspace_name // .bot.workspace_id // empty')
73
+ if [ -n "$me_workspace" ] && [ "$me_workspace" = "$WORKSPACE" ]; then
74
+ : ${substrate:=curl}
75
+ elif [ -n "$me_workspace" ]; then
76
+ echo "Warning: Notion token belongs to workspace '$me_workspace' but config declares '$WORKSPACE'. Skipping curl tier." >&2
77
+ fi
78
+ fi
79
+
80
+ # Fail loudly with actionable remediation if nothing works.
81
+ if [ -z "$substrate" ]; then
82
+ # Detect plugin enablement state for the suggestion.
83
+ plugin_enabled_global=$(jq -r '.enabledPlugins["notion@claude-plugins-official"] // false' ~/.claude/settings.json 2>/dev/null || echo "false")
84
+ plugin_enabled_project=$(jq -r '.enabledPlugins["notion@claude-plugins-official"] // false' .claude/settings.json 2>/dev/null || echo "false")
85
+ plugin_enabled_local=$(jq -r '.enabledPlugins["notion@claude-plugins-official"] // false' .claude/settings.local.json 2>/dev/null || echo "false")
86
+
87
+ cat >&2 <<EOF
88
+ Error: no Notion access substrate available for workspace '$WORKSPACE'.
89
+
90
+ Attempted:
91
+ MCP — $([ "$plugin_enabled_global" = "true" ] || [ "$plugin_enabled_project" = "true" ] || [ "$plugin_enabled_local" = "true" ] && echo "plugin enabled but not authenticated or cannot fetch configured prdDatabaseId" || echo "plugin not enabled in any settings.json scope")
92
+ curl — no NOTION_API_TOKEN found for $WORKSPACE (env, slug-suffixed env, or keychain) OR token belongs to a different workspace
93
+
94
+ Remediation paths (pick one):
95
+
96
+ 1. Install the Notion MCP plugin (local scope — per-developer, gitignored).
97
+ This is the simplest path for single-workspace developers.
98
+
99
+ Run in your terminal:
100
+
101
+ jq '.enabledPlugins["notion@claude-plugins-official"] = true' \\
102
+ .claude/settings.local.json 2>/dev/null > /tmp/s && \\
103
+ mv /tmp/s .claude/settings.local.json || \\
104
+ echo '{"enabledPlugins":{"notion@claude-plugins-official":true}}' > .claude/settings.local.json
105
+
106
+ Then restart Claude Code (or run /restart-mcp) to load the plugin, and
107
+ invoke 'mcp__plugin_notion_notion__authenticate' to complete OAuth.
108
+ Also share the configured prdDatabaseId with the integration via
109
+ the page's '•••' menu → Connections.
110
+
111
+ 2. Provision an internal-integration API token (headless / CI / multi-workspace).
112
+
113
+ Run /lisa:setup:notion — guided flow with clipboard-piped keychain store.
114
+
115
+ EOF
116
+ exit 1
117
+ fi
118
+ ```
119
+
120
+ ### Step 2 — Connection-match assertion
121
+
122
+ The substrate selection in Step 1 already verifies identity. This step is the explicit re-assertion before any operation runs — defensive in case substrate state changed since selection. For the curl tier, re-validate token-to-workspace pairing if more than a few minutes elapsed.
123
+
124
+ The workspace identifier stored in config is whatever stable string the user picked at setup time — typically `bot.workspace_name` (human-readable) for simplicity. If the workspace has been renamed in Notion, `setup-notion` re-detects and re-stores; the access skill surfaces the mismatch instead of silently authing as the wrong workspace.
125
+
126
+ ### Step 3 — Operation dispatch
127
+
128
+ When `$substrate=mcp`, route through Notion MCP tools. When `$substrate=curl`, hit the Notion REST API directly. All curl calls use `https://api.notion.com/v1/<path>`, `Notion-Version: 2022-06-28`, `Authorization: Bearer $TOKEN`.
129
+
130
+ Substrate columns: try the column matching `$substrate` first. If that column is `—` for the requested operation (no adapter), fall through to the other substrate if it's also available. If neither has an adapter, the operation is unsupported.
131
+
132
+ | Operation | MCP adapter | curl adapter |
133
+ |---|---|---|
134
+ | **Pages** | | |
135
+ | `read-page id:<I>` | `mcp__claude_ai_Notion__notion-fetch` | `GET /v1/pages/<I>` |
136
+ | `write-page payload:<P>` | `mcp__claude_ai_Notion__notion-update-page` | `PATCH /v1/pages/<I>` body `{ "properties": {...}, "archived": true/false }` |
137
+ | `archive-page id:<I>` | `mcp__claude_ai_Notion__notion-update-page` (with `archived: true`) | `PATCH /v1/pages/<I>` body `{ "archived": true }` |
138
+ | `append-blocks page_id:<P> children:<arr>` | (no direct equivalent) | `PATCH /v1/blocks/<P>/children` body `{ "children": <arr> }` |
139
+ | **Databases** | | |
140
+ | `read-database id:<I>` | `mcp__claude_ai_Notion__notion-fetch` | `GET /v1/databases/<I>` |
141
+ | `query-database id:<I> filter:<F> sort:<S>` | `mcp__claude_ai_Notion__notion-search` (with collection scope) | `POST /v1/databases/<I>/query` body `{ "filter": <F>, "sorts": <S>, "page_size": <N> }` |
142
+ | **Comments** | | |
143
+ | `list-comments block_id:<I>` | (MCP lacks a generic list-comments tool) | `GET /v1/comments?block_id=<I>` |
144
+ | `create-comment page_id:<I> rich_text:<arr>` | `mcp__claude_ai_Notion__notion-create-comment` (page-level) | `POST /v1/comments` body `{ "parent": { "page_id": "<I>" }, "rich_text": <arr> }` |
145
+ | `create-comment-on-block block_id:<I> rich_text:<arr>` | `mcp__claude_ai_Notion__notion-create-comment` (with block anchor) | `POST /v1/comments` body `{ "parent": { "block_id": "<I>" }, "rich_text": <arr> }` |
146
+ | **Search & users** | | |
147
+ | `search query:<Q> [filter:<F>]` | `mcp__claude_ai_Notion__notion-search` | `POST /v1/search` body `{ "query": "<Q>", "filter": <F or null> }` |
148
+ | `list-users` | — | `GET /v1/users` |
149
+ | `get-self` | — | `GET /v1/users/me` |
150
+
151
+ Operations not in this table are unsupported — add an adapter row before invoking. Adapters MUST return parsed JSON; never raw HTTP responses.
152
+
153
+ ### Step 4 — Return result
154
+
155
+ Wrap the JSON response in a `<result>` block for caller parsing. On HTTP non-2xx, prefix the error message with `Error:` and surface the HTTP status code plus Notion's response body verbatim.
156
+
157
+ ```bash
158
+ exec_op() {
159
+ local method="$1" path="$2" body="${3:-}"
160
+ local args=( -s -X "$method"
161
+ -H "Authorization: Bearer $TOKEN"
162
+ -H "Notion-Version: 2022-06-28" )
163
+ [ -n "$body" ] && args+=( -H "Content-Type: application/json" --data-binary "$body" )
164
+ local code=$(curl "${args[@]}" -o /tmp/notion-resp -w "%{http_code}" \
165
+ "https://api.notion.com/v1${path}")
166
+ if [ "${code:0:1}" != "2" ]; then
167
+ echo "Error: Notion API $method $path returned HTTP $code" >&2
168
+ cat /tmp/notion-resp >&2
169
+ return 1
170
+ fi
171
+ cat /tmp/notion-resp
172
+ }
173
+ ```
174
+
175
+ ## Invariants
176
+
177
+ - Caller skills never call `curl https://api.notion.com/...` or any `mcp__*notion*` tool directly. They invoke this skill via the Skill tool with an operation name and arguments.
178
+ - Substrate is selected per skill invocation following the tier ladder. The first tier that's available AND identity-matches `notion.workspaceId` wins.
179
+ - The connection-match check is mandatory at every tier. Skipping it (because "the user obviously meant this workspace") is forbidden — silent cross-workspace operations are exactly the multi-account hazard this design exists to prevent.
180
+ - API tokens never mutate. If the configured workspace's token is wrong or missing, fail loudly and tell the user to run `/lisa:setup:notion`.
181
+ - `Notion-Version` is pinned to `2022-06-28` — the version every existing notion-* skill targets. Bumping it is a coordinated change across the access skill and all callers.
182
+
183
+ ## Headless behavior
184
+
185
+ In a headless / non-interactive context (no TTY, `CI=true`, or `-p` mode), the MCP tier is unavailable (its OAuth flow needs a browser). The ladder collapses to curl + `NOTION_API_TOKEN`. Same skill code runs identically; only the substrate changes.
186
+
187
+ ## Per-page sharing prerequisite
188
+
189
+ Notion integrations only see pages that have been **explicitly shared** with them. If `read-page` or `query-database` returns a 404 or `object_not_found` error and the configured workspace is correct, the cause is almost always that the page/database wasn't shared with the integration. Surface this in the error message:
190
+
191
+ > Page <id> not visible to the integration. Open the page in Notion → "..." menu → Connections → add the lisa integration.
192
+
193
+ Do not paper over with a retry. Sharing is a one-time human action per database (or per page if the user prefers page-level sharing); failures here mean the user needs to act.