@codyswann/lisa 2.33.2 → 2.35.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (34) hide show
  1. package/dist/codex/plugin-marketplace-installer.d.ts.map +1 -1
  2. package/dist/codex/plugin-marketplace-installer.js +2 -0
  3. package/dist/codex/plugin-marketplace-installer.js.map +1 -1
  4. package/package.json +1 -1
  5. package/plugins/lisa/.claude-plugin/plugin.json +1 -1
  6. package/plugins/lisa/.codex-plugin/plugin.json +1 -1
  7. package/plugins/lisa/skills/jira-build-intake/SKILL.md +52 -5
  8. package/plugins/lisa/skills/jira-validate-ticket/SKILL.md +34 -3
  9. package/plugins/lisa/skills/linear-build-intake/SKILL.md +50 -5
  10. package/plugins/lisa/skills/linear-validate-issue/SKILL.md +33 -2
  11. package/plugins/lisa/skills/tracker-build-intake/SKILL.md +19 -1
  12. package/plugins/lisa-cdk/.claude-plugin/plugin.json +1 -1
  13. package/plugins/lisa-cdk/.codex-plugin/plugin.json +1 -1
  14. package/plugins/lisa-expo/.claude-plugin/plugin.json +1 -1
  15. package/plugins/lisa-expo/.codex-plugin/plugin.json +1 -1
  16. package/plugins/lisa-harper-fabric/.claude-plugin/plugin.json +1 -1
  17. package/plugins/lisa-harper-fabric/.codex-plugin/plugin.json +1 -1
  18. package/plugins/lisa-nestjs/.claude-plugin/plugin.json +1 -1
  19. package/plugins/lisa-nestjs/.codex-plugin/plugin.json +1 -1
  20. package/plugins/lisa-openclaw/.claude-plugin/plugin.json +1 -1
  21. package/plugins/lisa-openclaw/.codex-plugin/plugin.json +1 -1
  22. package/plugins/lisa-rails/.claude-plugin/plugin.json +1 -1
  23. package/plugins/lisa-rails/.codex-plugin/plugin.json +1 -1
  24. package/plugins/lisa-typescript/.claude-plugin/plugin.json +1 -1
  25. package/plugins/lisa-typescript/.codex-plugin/plugin.json +1 -1
  26. package/plugins/lisa-wiki/.claude-plugin/plugin.json +1 -1
  27. package/plugins/lisa-wiki/.codex-plugin/plugin.json +1 -1
  28. package/plugins/lisa-wiki/commands/setup/wiki.md +6 -0
  29. package/plugins/src/base/skills/jira-build-intake/SKILL.md +52 -5
  30. package/plugins/src/base/skills/jira-validate-ticket/SKILL.md +34 -3
  31. package/plugins/src/base/skills/linear-build-intake/SKILL.md +50 -5
  32. package/plugins/src/base/skills/linear-validate-issue/SKILL.md +33 -2
  33. package/plugins/src/base/skills/tracker-build-intake/SKILL.md +19 -1
  34. package/plugins/src/wiki/commands/setup/wiki.md +6 -0
@@ -1 +1 @@
1
- {"version":3,"file":"plugin-marketplace-installer.d.ts","sourceRoot":"","sources":["../../src/codex/plugin-marketplace-installer.ts"],"names":[],"mappings":"AAWA,6EAA6E;AAC7E,eAAO,MAAM,sBAAsB,QAIlC,CAAC;AAwBF,2CAA2C;AAC3C,MAAM,WAAW,wBAAwB;IACvC,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;IAC1B,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;CAChC;AAED;;;;;GAKG;AACH,wBAAsB,uBAAuB,CAC3C,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,MAAM,GACd,OAAO,CAAC,wBAAwB,CAAC,CAcnC;AAED;;;;;;GAMG;AACH,wBAAgB,oBAAoB,CAClC,QAAQ,EAAE,OAAO,EACjB,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,MAAM,GACd,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAkBzB"}
1
+ {"version":3,"file":"plugin-marketplace-installer.d.ts","sourceRoot":"","sources":["../../src/codex/plugin-marketplace-installer.ts"],"names":[],"mappings":"AAWA,6EAA6E;AAC7E,eAAO,MAAM,sBAAsB,QAIlC,CAAC;AA0BF,2CAA2C;AAC3C,MAAM,WAAW,wBAAwB;IACvC,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;IAC1B,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;CAChC;AAED;;;;;GAKG;AACH,wBAAsB,uBAAuB,CAC3C,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,MAAM,GACd,OAAO,CAAC,wBAAwB,CAAC,CAcnC;AAED;;;;;;GAMG;AACH,wBAAgB,oBAAoB,CAClC,QAAQ,EAAE,OAAO,EACjB,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,MAAM,GACd,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAkBzB"}
@@ -19,6 +19,7 @@ const LISA_CODEX_PLUGINS = [
19
19
  "lisa-cdk",
20
20
  "lisa-harper-fabric",
21
21
  "lisa-rails",
22
+ "lisa-wiki",
22
23
  ];
23
24
  /** Marketplace category should match each generated `.codex-plugin` manifest. */
24
25
  const LISA_CODEX_PLUGIN_CATEGORIES = {
@@ -29,6 +30,7 @@ const LISA_CODEX_PLUGIN_CATEGORIES = {
29
30
  "lisa-cdk": "Coding",
30
31
  "lisa-harper-fabric": "Coding",
31
32
  "lisa-rails": "Coding",
33
+ "lisa-wiki": "Productivity",
32
34
  };
33
35
  /**
34
36
  * Install or merge Lisa's Codex marketplace entries.
@@ -1 +1 @@
1
- {"version":3,"file":"plugin-marketplace-installer.js","sourceRoot":"","sources":["../../src/codex/plugin-marketplace-installer.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"plugin-marketplace-installer.js","sourceRoot":"","sources":["../../src/codex/plugin-marketplace-installer.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -82,7 +82,7 @@
82
82
  "lodash": ">=4.18.1"
83
83
  },
84
84
  "name": "@codyswann/lisa",
85
- "version": "2.33.2",
85
+ "version": "2.35.0",
86
86
  "description": "Claude Code governance framework that applies guardrails, guidance, and automated enforcement to projects",
87
87
  "main": "dist/index.js",
88
88
  "exports": {
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa",
3
- "version": "2.33.2",
3
+ "version": "2.35.0",
4
4
  "description": "Universal governance — agents, skills, commands, hooks, and rules for all projects",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa",
3
- "version": "2.33.2",
3
+ "version": "2.35.0",
4
4
  "description": "Universal governance: agents, skills, commands, hooks, and rules for all projects.",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: jira-build-intake
3
- description: "Symmetric counterpart to notion-prd-intake on the JIRA side. Scans a JIRA project (or JQL filter) for tickets in the configured `ready` status, claims each by transitioning to the configured `claimed` status, runs the implementation/build flow via jira-agent, and transitions to the configured `done` status on completion. The `ready` status is the human-flipped signal that a TODO ticket is truly ready for development — mirroring how Notion PRDs work product Draft → Ready → (us) In Review → Blocked|Ticketed."
3
+ description: "Symmetric counterpart to notion-prd-intake on the JIRA side. Scans a JIRA project (or JQL filter) for tickets in the configured `ready` status, claims each by transitioning to the configured `claimed` status, runs the implementation/build flow via jira-agent, and transitions to the configured `done` status on completion. 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 status is skipped or safe-blocked with a lifecycle-repair comment, never claimed. The `ready` status is the human-flipped signal that a TODO ticket is truly ready for development — mirroring how Notion PRDs work product Draft → Ready → (us) In Review → Blocked|Ticketed."
4
4
  allowed-tools: ["Skill", "Bash"]
5
5
  ---
6
6
 
@@ -118,7 +118,50 @@ If empty, report `"No tickets with Status=$READY. Nothing to do."` and exit. Thi
118
118
 
119
119
  ### Phase 3 — Process each ready ticket (serial)
120
120
 
121
- #### 3a. Claim
121
+ #### 3a. Leaf-only claim gate (skip / safe-block containers)
122
+
123
+ 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 status (e.g. `Ready` applied before this rule existed, or hand-applied to an Epic/Story) 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:jira-write-ticket` and the validate-time S15 gate in `lisa:jira-validate-ticket`; all three cite the same rule so the classification never drifts. **Never silently implement a container.**
124
+
125
+ Run this gate **before** the claim transition, for every candidate ticket. Do NOT transition, comment "Claimed", or invoke `lisa:jira-agent` for a ticket that fails the gate.
126
+
127
+ **Resolve container vs. leaf — structural first, then nominal.** Per `leaf-only-lifecycle` the classification is structural: a ticket is a **container** if it has **open** child work, whatever its declared type; otherwise the **issue type** decides. Resolve child work using the same hierarchy `lisa:jira-read-ticket` uses — JIRA's native Epic → Story → Sub-task parentage (Epic link / parent field for Stories under an Epic, and the subtask relationship for Sub-tasks under a Story/Task). Issue links (`blocks` / `is blocked by`) express cross-item dependencies and are **not** parentage — do not count them as children.
128
+
129
+ Fetch the ticket's children via `lisa:atlassian-access` `operation: search-issues` with a JQL that resolves both subtasks and Epic-linked Stories, then count those still open (not in a resolved/Done status):
130
+
131
+ ```bash
132
+ # Children of <TICKET>: native subtasks plus, for an Epic, its linked Stories.
133
+ # (parent = <TICKET>) covers Sub-tasks and child issues; ("Epic Link" = <TICKET>)
134
+ # covers Stories under an Epic on JIRA instances that expose the Epic Link field.
135
+ CHILDREN_JQL='(parent = "<TICKET>" OR "Epic Link" = "<TICKET>")'
136
+ # Count children whose status is NOT a resolved/terminal one. A parent whose
137
+ # children are all Done is no longer holding open work and rolls up via
138
+ # leaf-only-lifecycle's rollup, not here.
139
+ OPEN_CHILDREN_JQL="${CHILDREN_JQL} AND statusCategory != Done"
140
+ ```
141
+
142
+ Invoke `lisa:atlassian-access` `operation: search-issues jql: "<OPEN_CHILDREN_JQL>"` and let `OPEN_CHILDREN` be the count of returned issues (0 if none). If the JQL cannot resolve the `Epic Link` field on this instance (older JIRA / team-managed projects expose parentage differently), fall back to the parentage `lisa:jira-read-ticket` derives and treat the ticket as a container if any derived child is open. Note "Epic Link unavailable — parentage derived" so the operator knows how children were resolved.
143
+
144
+ Classify and act (first match wins). The issue type comes from the ticket's `issuetype` field (`Epic`, `Story`, `Spike`, `Bug`, `Task`, `Sub-task`, `Improvement`):
145
+
146
+ | Condition | Class | Action |
147
+ |---|---|---|
148
+ | `OPEN_CHILDREN > 0` (open child work, any type) | **Container** | **Skip / safe-block — do NOT claim** |
149
+ | no open children AND type ∈ {Epic, Story, Spike} | **Childless container-type** | **Skip / safe-block — do NOT claim** |
150
+ | no open children AND type ∈ {Bug, Task, Sub-task, Improvement} (or no recognized type) | **Leaf work unit** | **Proceed to 3b claim** |
151
+
152
+ 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.
153
+
154
+ **Safe-block (default action for a flagged container).** Leave the build-ready status in place (don't silently transition it away — that hides the lifecycle error), post a single lifecycle-repair comment, and record the ticket under "Skipped (container)" in the summary. Do NOT transition to `$CLAIMED`. Keep the comment idempotent — skip posting if an identical `[claude-build-intake]` lifecycle-repair comment already exists on the ticket, so a re-entrant cycle doesn't spam it.
155
+
156
+ Post via `lisa:atlassian-access` `operation: comment key: <TICKET> body: "<message>"` with:
157
+
158
+ ```text
159
+ [claude-build-intake] Not claimed: this ticket carries the build-ready status ($READY) but is a container with open child work (or a childless Epic/Story/Spike), which violates the leaf-only-lifecycle rule. Build-ready (status:ready) is leaf-only per leaf-only-lifecycle — an agent claims and implements leaves, never a container. Repair: move $READY off this parent onto its leaf children (or, for a childless Epic/Story/Spike, decompose it into leaf children or reclassify it to a leaf type). A parent's lifecycle state rolls up from its children and is never set to ready directly.
160
+ ```
161
+
162
+ This gate never blocks a legitimate flat Task/Bug: those have no open children and a leaf type, so they fall straight through to the claim in 3b.
163
+
164
+ #### 3b. Claim
122
165
 
123
166
  Transition the ticket from `$READY` to `$CLAIMED` by invoking `lisa:atlassian-access` `operation: transition key: <TICKET> to: "$CLAIMED"`.
124
167
  - Post a `[claude-build-intake]` comment via `lisa:atlassian-access` `operation: comment key: <TICKET> body: "Claimed by Claude. Starting build."`
@@ -126,7 +169,7 @@ Transition the ticket from `$READY` to `$CLAIMED` by invoking `lisa:atlassian-ac
126
169
 
127
170
  If the transition fails (permission, missing transition, race), log under "Errors" in the cycle summary and skip this ticket. **Do not invoke the build flow on a ticket you didn't successfully claim.**
128
171
 
129
- #### 3b. Run the build flow
172
+ #### 3c. Run the build flow
130
173
 
131
174
  Invoke the `lisa:jira-agent` (existing per-ticket lifecycle agent) with the ticket key. `lisa:jira-agent` owns:
132
175
  - Reading the full ticket graph (`lisa:jira-read-ticket`)
@@ -142,7 +185,7 @@ Wait for `lisa:jira-agent` to return. Capture its outcome:
142
185
  - **Blocked by ticket-triage ambiguities** — `lisa:jira-agent` posts findings and stops. The ticket stays in `$CLAIMED`. Surface to human; do not auto-transition. Record under "Errors" with reason `"Triage found ambiguities — see comments on <ticket-key>"`.
143
186
  - **Errored** — exception, missing config, etc. Leave the ticket in `$CLAIMED` for human investigation. Record under "Errors" with the exception summary.
144
187
 
145
- #### 3c. Transition to $DONE (only on Success)
188
+ #### 3d. Transition to $DONE (only on Success)
146
189
 
147
190
  If `lisa:jira-agent` returned Success:
148
191
  1. Resolve `$DONE` for this ticket's PR base branch using the Workflow resolution algorithm above. If env can't be resolved and `done` is env-keyed, record an Error and skip this transition — never guess.
@@ -151,7 +194,7 @@ If `lisa:jira-agent` returned Success:
151
194
 
152
195
  For any non-Success outcome, do NOT transition. The ticket sits in `$CLAIMED` (or wherever `lisa:jira-agent` left it for the Blocked case) — the cycle's job is done; humans take it from there.
153
196
 
154
- #### 3d. Continue
197
+ #### 3e. Continue
155
198
 
156
199
  Move to the next ready ticket. One ticket failing does not stop others.
157
200
 
@@ -167,6 +210,8 @@ Cycle completed: <ISO timestamp>
167
210
  Tickets processed: <n>
168
211
  - $DONE (build complete, PR ready): <n>
169
212
  - <ticket-key> <summary> → PR <URL>
213
+ - Skipped (container — leaf-only-lifecycle): <n>
214
+ - <ticket-key> <summary> — build-ready on a parent with open child work; lifecycle-repair comment posted
170
215
  - Blocked (pre-flight verify failed): <n>
171
216
  - <ticket-key> <summary> — see ticket comments
172
217
  - Held (triage found ambiguities): <n>
@@ -179,6 +224,7 @@ Total PRs opened: <n>
179
224
 
180
225
  ## Idempotency & safety
181
226
 
227
+ - **Leaf-only claim gate runs first**: Phase 3a classifies each candidate before any claim; a container with open child work (or a childless Epic/Story/Spike) is skipped/safe-blocked, never claimed (the `leaf-only-lifecycle` rule's claim-time arm). The safe-block comment is idempotent — a re-entrant cycle does not re-post it.
182
228
  - **Claim-first ordering**: `$CLAIMED` set BEFORE `lisa:jira-agent` invocation — no double-pickup.
183
229
  - **No writes outside the lifecycle**: this skill only transitions `$READY → $CLAIMED` and `$CLAIMED → $DONE`. Every other status change is owned by `lisa:jira-agent` (which suggests transitions but only auto-transitions on the verify-FAIL path).
184
230
  - **Failure isolation**: per-ticket exceptions caught and recorded; the cycle continues.
@@ -211,6 +257,7 @@ If a ready-equivalent status does not exist in the JIRA project's workflow, this
211
257
 
212
258
  ## Rules
213
259
 
260
+ - **Claim leaves only.** Per the `leaf-only-lifecycle` rule, never claim a container — a ticket with open child work, or a childless Epic/Story/Spike — even if it carries the build-ready status. Skip or safe-block it (Phase 3a); never silently implement a container.
214
261
  - Never transition a ticket the cycle didn't claim. The `$CLAIMED` transition is the signature of cycle ownership.
215
262
  - Never bypass `lisa:jira-agent` to do build work directly. `lisa:jira-agent` owns the per-ticket lifecycle (read, verify, triage, route, sync, evidence). This skill is the dispatcher, not the builder.
216
263
  - Never auto-transition past `$DONE`. Downstream statuses are owned by QA / product / a future verification-intake skill — not this one.
@@ -60,9 +60,11 @@ authenticated_surface: true # → requires Sign-in Required
60
60
  artifacts_attached: true # → requires Source Precedence section
61
61
  links: [{ key: "PROJ-99", type: "is blocked by" }] # known issue links (may be empty)
62
62
  remote_links: [{ url: "https://github.com/...", title: "PR #42" }]
63
+ build_ready: true # caller asserts the build-ready role (status:ready) is/would be applied — see S15
64
+ child_refs: ["PROJ-601", "PROJ-602"] # known child work (sub-tasks / "is blocked by" parentage) — see S15
63
65
  ```
64
66
 
65
- If the caller passes only a ticket key, fetch the ticket via `lisa:atlassian-access` `operation: read-ticket key: <KEY>`, derive the same fields from the fetched data, then run gates.
67
+ If the caller passes only a ticket key, fetch the ticket via `lisa:atlassian-access` `operation: read-ticket key: <KEY>`, derive the same fields from the fetched data — including `build_ready` (label set contains `status:ready`) and `child_refs` (sub-tasks plus `is blocked by` parentage, resolved as in `lisa:jira-read-ticket`) so S15 can classify the ticket — then run gates.
66
68
 
67
69
  ## Gates
68
70
 
@@ -86,6 +88,7 @@ Each gate is tagged with a fixed `category` and a `product_relevant` boolean. Ca
86
88
  | S12 Source Precedence | `design-ux` | true |
87
89
  | S13 Relationship Search | `dependency` | true |
88
90
  | S14 Evidence manifest binding (leaf work units) | `acceptance-criteria` | true |
91
+ | S15 Leaf-only build-ready | `structural` | false |
89
92
  | F1 Issue type valid in project | `structural` | false |
90
93
  | F2 Epic parent exists and is an Epic | `structural` | false |
91
94
  | F3 Linked tickets exist | `structural` | false |
@@ -199,6 +202,33 @@ FAIL when the Validation Journey is present but declares zero `[EVIDENCE: name]`
199
202
 
200
203
  This gate depends on S11. It is `N/A` for Epic / 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:jira-add-journey`.
201
204
 
205
+ #### S15 — Leaf-only build-ready
206
+
207
+ Enforces the build-side of the vendor-neutral `leaf-only-lifecycle` rule: **only a leaf work unit may carry the build-ready role.** This is the symmetric write-side guard for the JIRA validator — a stale or hand-applied `status:ready` label on a container is a lifecycle error and must FAIL here, regardless of how the ticket was produced. (Mirrors the "Build-ready label is leaf-only" rule that `lisa:jira-write-ticket` applies at write time.)
208
+
209
+ **When the gate applies.** Run S15 whenever the ticket is build-ready — i.e. `build_ready = true`, or the spec/live labels include `status:ready`. If the ticket is not build-ready, S15 is `N/A` (nothing claims a non-ready ticket, so the invariant is vacuous).
210
+
211
+ **Resolve container vs. leaf — structural first, then nominal.** Per `leaf-only-lifecycle` the classification is structural: an item is a **container** if it has child work, whatever its declared type; otherwise the **issue type** decides. Determine child work from (in order) `child_refs`, native sub-tasks, and `is blocked by` / parent references — the same hierarchy resolution `lisa:jira-read-ticket` uses. When validating a live key, query sub-tasks alongside the ticket fetch.
212
+
213
+ Apply this decision and FAIL the two invariant-violating cases:
214
+
215
+ 1. **Container with child work + build-ready** — `issue_type ∈ {Epic, Story, Spike}` OR child work is present (any type that has children), AND build-ready. FAIL. A parent organizes work; it is never claimed and implemented directly. Its lifecycle state rolls up from its children.
216
+ 2. **Childless container-type + build-ready** — `issue_type ∈ {Epic, Story, Spike}` with **no** child work, AND build-ready. Still FAIL: these types are coordination containers by design, and an empty one is an incomplete decomposition, not an implementable unit (the childless-parent exception in `leaf-only-lifecycle` does **not** promote an Epic/Story/Spike to build-ready).
217
+
218
+ PASS (the childless-parent exception) when the ticket is build-ready and is a **leaf work unit**: `issue_type ∈ {Bug, Task, Sub-task, Improvement}` AND has **no** child work. A flat Task or Bug with no sub-tasks is a valid build-ready leaf and must not be stranded.
219
+
220
+ | issue_type | has child work | build-ready | S15 |
221
+ |---|---|---|---|
222
+ | Bug / Task / Sub-task / Improvement | no | yes | **PASS** (leaf) |
223
+ | Bug / Task / Sub-task / Improvement | yes | yes | **FAIL** (structurally a container) |
224
+ | Epic / Story / Spike | yes | yes | **FAIL** (container with children) |
225
+ | Epic / Story / Spike | no | yes | **FAIL** (childless container-type, exception does not apply) |
226
+ | any | any | no | **N/A** (not build-ready) |
227
+
228
+ Remediation: `"Build-ready (status:ready) is leaf-only per leaf-only-lifecycle. Move status:ready off this container onto its leaf children (or, for a childless Epic/Story/Spike, decompose it into leaf children or reclassify it to a leaf type); a parent's lifecycle state rolls up from its children and is never set to ready directly."`
229
+
230
+ `product_relevant: false` — a build-ready container is a lifecycle/decomposition error for the caller to repair, not a product question.
231
+
202
232
  ### Feasibility Gates (require JIRA lookups; skip in dry-run if requested)
203
233
 
204
234
  #### F1 — Issue type valid in project
@@ -219,7 +249,7 @@ Use the same project-issue-type-metadata lookup from F1 (via `lisa:atlassian-acc
219
249
 
220
250
  ## Execution
221
251
 
222
- 1. Parse `$ARGUMENTS`. If it's a ticket key, fetch the ticket via `lisa:atlassian-access` `operation: read-ticket` and derive the spec from the fetched fields. Otherwise parse the YAML spec.
252
+ 1. Parse `$ARGUMENTS`. If it's a ticket key, fetch the ticket via `lisa:atlassian-access` `operation: read-ticket` and derive the spec from the fetched fields — including `build_ready` (label set contains `status:ready`) and `child_refs` (sub-tasks plus `is blocked by` parentage, resolved as in `lisa:jira-read-ticket`) so S15 can classify the ticket. Otherwise parse the YAML spec.
223
253
  2. If any feasibility gate will run, invoke `lisa:atlassian-access` `operation: list-sites` once to confirm the configured site is reachable (it enforces connection match against `.lisa.config.json`).
224
254
  3. Run every Specification gate in order. Collect PASS / FAIL / N/A with a one-line reason.
225
255
  4. Unless the caller passed `--spec-only` (dry-run), run every Feasibility gate. Collect results.
@@ -247,6 +277,7 @@ Output is a single fenced text block. Callers parse it; do not add free-form pro
247
277
  - [PASS|FAIL|N/A] S12 Source Precedence — <one-line reason>
248
278
  - [PASS|FAIL|N/A] S13 Relationship Search — <one-line reason>
249
279
  - [PASS|FAIL|N/A] S14 Evidence manifest binding — <one-line reason>
280
+ - [PASS|FAIL|N/A] S15 Leaf-only build-ready — <one-line reason>
250
281
 
251
282
  ### Feasibility Gates (omit this section when --spec-only)
252
283
  - [PASS|FAIL|N/A] F1 Issue type valid in project — <one-line reason>
@@ -271,7 +302,7 @@ The verdict is `PASS` if and only if every applicable gate is `PASS`. Any `FAIL`
271
302
 
272
303
  ### Failure-detail fields
273
304
 
274
- - **gate**: the gate ID (`S1`–`S14`, `F1`–`F4`).
305
+ - **gate**: the gate ID (`S1`–`S15`, `F1`–`F4`).
275
306
  - **category**: the gate's fixed category from the table above. Callers use this to label or filter comments — `product-clarity`, `acceptance-criteria`, `design-ux`, `scope`, `dependency`, `data`, `technical`, or `structural`.
276
307
  - **product_relevant**: matches the gate's table entry. `false` means the failure is an internal data-quality problem (e.g., the agent built a malformed spec, an issue type is invalid in the project) and the caller should fix it without bothering the product team. `true` means the PRD needs product input to resolve.
277
308
  - **what**: plain-language description of the issue. No gate IDs, no JIRA jargon, no engineering shorthand. A product owner reading this on a Notion comment should understand what is unclear and why.
@@ -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 each by relabeling to the configured `claimed` label, runs the implementation/build flow via lisa:linear-agent, and relabels to the configured `done` label on completion. 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."
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 each by relabeling to the configured `claimed` label, runs the implementation/build flow via lisa:linear-agent, and relabels to the configured `done` label on completion. 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
 
@@ -128,7 +128,48 @@ If empty, report `"No Linear Issues labeled $READY. Nothing to do."` and exit. C
128
128
 
129
129
  ### Phase 3 — Process each ready Issue (serial)
130
130
 
131
- #### 3a. Claim
131
+ #### 3a. Leaf-only claim gate (skip / safe-block containers)
132
+
133
+ 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
+
135
+ Run this gate **before** the claim relabel, for every candidate Issue. Do NOT relabel, comment "Claimed", or invoke `lisa:linear-agent` for an Issue that fails the gate.
136
+
137
+ **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
+
139
+ Fetch the Issue's sub-issues via `mcp__linear-server__get_issue` (which returns the children) or `mcp__linear-server__list_issues({parentId: <issueId>})`, then count those still open (Linear `state.type` not in the completed/canceled set):
140
+
141
+ ```text
142
+ # Children of <issueId>: native sub-issues via parentId.
143
+ # Count children whose Linear state.type is NOT terminal ("completed" / "canceled").
144
+ # A parent whose children are all completed is no longer holding open work and
145
+ # rolls up via leaf-only-lifecycle's rollup, not here.
146
+ OPEN_CHILDREN = count(list_issues({parentId: <issueId>})
147
+ where state.type not in {"completed", "canceled"})
148
+ ```
149
+
150
+ For a Project-level parent (an Issue that itself anchors a `projectId` grouping rather than a `parentId` tree), resolve membership the same way `lisa:linear-read-issue` does and treat the parent as a container if any grouped Issue is still open. If sub-issue resolution is unavailable, fall back to the parentage `lisa:linear-read-issue` derives and treat the Issue as a container if any derived child is open. Note "sub-issues unavailable — parentage derived" so the operator knows how children were resolved.
151
+
152
+ Classify and act (first match wins). The type comes from the Issue's `type:` label (`type:Epic`, `type:Story`, `type:Spike`, `type:Bug`, `type:Task`, `type:Sub-task`, `type:Improvement`):
153
+
154
+ | Condition | Class | Action |
155
+ |---|---|---|
156
+ | `OPEN_CHILDREN > 0` (open child work, any type) | **Container** | **Skip / safe-block — do NOT claim** |
157
+ | no open children AND type ∈ {Epic, Story, Spike} | **Childless container-type** | **Skip / safe-block — do NOT claim** |
158
+ | no open children AND type ∈ {Bug, Task, Sub-task, Improvement} (or no `type:` label) | **Leaf work unit** | **Proceed to 3b claim** |
159
+
160
+ 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
+
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, and record the Issue under "Skipped (container)" in the summary. 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
+
164
+ Post via `mcp__linear-server__save_comment` with:
165
+
166
+ ```text
167
+ [claude-build-intake] Not claimed: this Issue carries the build-ready label ($READY) but is a container with open child work (or a childless Epic/Story/Spike), which violates the leaf-only-lifecycle rule. Build-ready (status:ready) is leaf-only per leaf-only-lifecycle — an agent claims and implements leaves, never a container. Repair: move $READY off this parent onto its leaf children (or, for a childless Epic/Story/Spike, decompose it into leaf children or reclassify it to a leaf type). A parent's lifecycle state rolls up from its children and is never set to ready directly.
168
+ ```
169
+
170
+ This gate never blocks a legitimate flat Task/Bug: those have no open children and a leaf `type:`, so they fall straight through to the claim in 3b.
171
+
172
+ #### 3b. Claim
132
173
 
133
174
  Update labels via `mcp__linear-server__save_issue`: remove `$READY`, add `$CLAIMED`. Resolve label IDs via `list_issue_labels` (create `$CLAIMED` if missing).
134
175
 
@@ -138,7 +179,7 @@ This is the idempotency lock — a re-entrant cycle's `label: $READY` filter wil
138
179
 
139
180
  If the relabel fails (permission, race), record under "Errors" and skip. **Do not invoke the build flow on an Issue you didn't successfully claim.**
140
181
 
141
- #### 3b. Run the build flow
182
+ #### 3c. Run the build flow
142
183
 
143
184
  Invoke `lisa:linear-agent` (per-Issue lifecycle agent) with the Issue identifier. `lisa:linear-agent` owns:
144
185
  - Reading the full Issue graph (`lisa:linear-read-issue`)
@@ -154,7 +195,7 @@ Wait for the agent to return. Capture its outcome:
154
195
  - **Blocked by ticket-triage ambiguities** — agent posts findings and stops. The Issue stays at `$CLAIMED`. Surface to human; do not auto-transition. Record under "Errors".
155
196
  - **Errored** — exception, missing config, etc. Leave at `$CLAIMED`. Record with exception summary.
156
197
 
157
- #### 3c. Relabel to $DONE (only on Success)
198
+ #### 3d. Relabel to $DONE (only on Success)
158
199
 
159
200
  If `lisa:linear-agent` returned Success:
160
201
  1. Resolve `$DONE` for this issue's PR base branch using the Workflow resolution algorithm above. If env can't be resolved and `done` is env-keyed, record an Error and skip this transition — never guess.
@@ -163,7 +204,7 @@ If `lisa:linear-agent` returned Success:
163
204
 
164
205
  For any non-Success outcome, do NOT transition. The Issue sits where the agent left it — humans take it from there.
165
206
 
166
- #### 3d. Continue
207
+ #### 3e. Continue
167
208
 
168
209
  Move to the next ready Issue. One Issue failing does not stop others.
169
210
 
@@ -179,6 +220,8 @@ Cycle completed: <ISO timestamp>
179
220
  Issues processed: <n>
180
221
  - $DONE (build complete, PR ready): <n>
181
222
  - <ID> <title> → PR <URL>
223
+ - Skipped (container — leaf-only-lifecycle): <n>
224
+ - <ID> <title> — build-ready on a parent with open child work; lifecycle-repair comment posted
182
225
  - status:blocked (pre-flight verify failed): <n>
183
226
  - <ID> <title> — see Issue comments
184
227
  - Held (triage found ambiguities): <n>
@@ -191,6 +234,7 @@ Total PRs opened: <n>
191
234
 
192
235
  ## Idempotency & safety
193
236
 
237
+ - **Leaf-only claim gate runs first**: Phase 3a classifies each candidate before any claim; a container with open child work (or a childless Epic/Story/Spike) is skipped/safe-blocked, never claimed (the `leaf-only-lifecycle` rule's claim-time arm). The safe-block comment is idempotent — a re-entrant cycle does not re-post it.
194
238
  - **Claim-first ordering**: `$CLAIMED` set BEFORE agent invocation — no double-pickup.
195
239
  - **No writes outside the lifecycle**: this skill only adds/removes `$READY`, `$CLAIMED`, `$DONE`. Every other label change (and the native state) is owned by the agent or `lisa:linear-evidence`.
196
240
  - **Failure isolation**: per-Issue exceptions caught and recorded; the cycle continues.
@@ -210,6 +254,7 @@ If the team hasn't adopted these labels, the first run exits with an adoption hi
210
254
 
211
255
  ## Rules
212
256
 
257
+ - **Claim leaves only.** Per the `leaf-only-lifecycle` rule, never claim a container — an Issue with open child work, or a childless Epic/Story/Spike — even if it carries the build-ready label. Skip or safe-block it (Phase 3a); never silently implement a container.
213
258
  - Never relabel an Issue the cycle didn't claim. The `$CLAIMED` transition is the signature of cycle ownership.
214
259
  - Never bypass `lisa:linear-agent` to do build work directly. The agent owns the per-Issue lifecycle.
215
260
  - Never auto-transition past `$DONE`. Downstream labels are owned by QA / product / a future verification-intake skill.
@@ -61,9 +61,11 @@ authenticated_surface: true # → requires Sign-in Required
61
61
  artifacts_attached: true # → requires Source Precedence section
62
62
  relations: [{ id: "ENG-99", type: "blocked_by" }] # known issue relations (may be empty)
63
63
  remote_links: [{ url: "https://github.com/...", title: "PR #42" }]
64
+ build_ready: true # caller asserts the build-ready role (status:ready) is/would be applied — see S15
65
+ child_refs: ["ENG-601", "ENG-602"] # known child work (sub-issues / project-member issues / blocked_by parentage) — see S15
64
66
  ```
65
67
 
66
- If the caller passes only an identifier, fetch the item via `mcp__linear-server__get_issue` (Issue) or `mcp__linear-server__get_project` (Project), derive the same fields from the fetched data, then run gates.
68
+ If the caller passes only an identifier, fetch the item via `mcp__linear-server__get_issue` (Issue) or `mcp__linear-server__get_project` (Project), derive the same fields from the fetched data — including `build_ready` (label set contains `status:ready`) and `child_refs` (sub-issues, project-member issues, plus `blocked_by` parentage, resolved as in `lisa:linear-read-issue`) so S15 can classify the item — then run gates.
67
69
 
68
70
  ## Gates
69
71
 
@@ -87,6 +89,7 @@ Each gate is tagged with a fixed `category` and a `product_relevant` boolean. Ca
87
89
  | S12 Source Precedence | `design-ux` | true |
88
90
  | S13 Relationship Search | `dependency` | true |
89
91
  | S14 Evidence manifest binding (leaf work units) | `acceptance-criteria` | true |
92
+ | S15 Leaf-only build-ready | `structural` | false |
90
93
  | F1 Issue type valid in team | `structural` | false |
91
94
  | F2 Project parent exists and is in same team | `structural` | false |
92
95
  | F3 Linked items exist | `structural` | false |
@@ -200,6 +203,33 @@ FAIL when the Validation Journey is present but declares zero `[EVIDENCE: name]`
200
203
 
201
204
  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
205
 
206
+ #### S15 — Leaf-only build-ready
207
+
208
+ Enforces the build-side of the vendor-neutral `leaf-only-lifecycle` rule: **only a leaf work unit may carry the build-ready role.** This is the symmetric write-side guard for the Linear validator — a stale or hand-applied `status:ready` label on a container is a lifecycle error and must FAIL here, regardless of how the item was produced. (Mirrors the "Build-ready label is leaf-only" rule that `lisa:linear-write-issue` applies at write time.)
209
+
210
+ **When the gate applies.** Run S15 whenever the item is build-ready — i.e. `build_ready = true`, or the spec/live labels include `status:ready`. If the item is not build-ready, S15 is `N/A` (nothing claims a non-ready item, so the invariant is vacuous).
211
+
212
+ **Resolve container vs. leaf — structural first, then nominal.** Per `leaf-only-lifecycle` the classification is structural: an item is a **container** if it has child work, whatever its declared type; otherwise the **issue type** decides. Determine child work from (in order) `child_refs`, native sub-issues, project-member issues (an Epic is modeled as a Linear Project), and `blocked_by` / parent references — the same hierarchy resolution `lisa:linear-read-issue` uses. When validating a live identifier, query sub-issues / project members alongside the item fetch.
213
+
214
+ Apply this decision and FAIL the two invariant-violating cases:
215
+
216
+ 1. **Container with child work + build-ready** — `issue_type ∈ {Epic, Story, Spike}` OR child work is present (any type that has children), AND build-ready. FAIL. A parent organizes work; it is never claimed and implemented directly. Its lifecycle state rolls up from its children.
217
+ 2. **Childless container-type + build-ready** — `issue_type ∈ {Epic, Story, Spike}` with **no** child work, AND build-ready. Still FAIL: these types are coordination containers by design, and an empty one is an incomplete decomposition, not an implementable unit (the childless-parent exception in `leaf-only-lifecycle` does **not** promote an Epic/Story/Spike to build-ready).
218
+
219
+ PASS (the childless-parent exception) when the item is build-ready and is a **leaf work unit**: `issue_type ∈ {Bug, Task, Sub-task, Improvement}` AND has **no** child work. A flat Task or Bug with no sub-issues is a valid build-ready leaf and must not be stranded.
220
+
221
+ | issue_type | has child work | build-ready | S15 |
222
+ |---|---|---|---|
223
+ | Bug / Task / Sub-task / Improvement | no | yes | **PASS** (leaf) |
224
+ | Bug / Task / Sub-task / Improvement | yes | yes | **FAIL** (structurally a container) |
225
+ | Epic / Story / Spike | yes | yes | **FAIL** (container with children) |
226
+ | Epic / Story / Spike | no | yes | **FAIL** (childless container-type, exception does not apply) |
227
+ | any | any | no | **N/A** (not build-ready) |
228
+
229
+ Remediation: `"Build-ready (status:ready) is leaf-only per leaf-only-lifecycle. Move status:ready off this container onto its leaf children (or, for a childless Epic/Story/Spike, decompose it into leaf children or reclassify it to a leaf type); a parent's lifecycle state rolls up from its children and is never set to ready directly."`
230
+
231
+ `product_relevant: false` — a build-ready container is a lifecycle/decomposition error for the caller to repair, not a product question.
232
+
203
233
  ### Feasibility Gates (require Linear lookups; skip in dry-run if requested)
204
234
 
205
235
  #### F1 — Issue type valid in team
@@ -224,7 +254,7 @@ For each label referenced (`status:*`, `component:<name>`, `prd-*`), confirm via
224
254
 
225
255
  ## Execution
226
256
 
227
- 1. Parse `$ARGUMENTS`. If it's an identifier, fetch the item and derive the spec from the fetched fields. Otherwise parse the YAML spec.
257
+ 1. Parse `$ARGUMENTS`. If it's an identifier, fetch the item and derive the spec from the fetched fields — including `build_ready` (label set contains `status:ready`) and `child_refs` (sub-issues, project-member issues, plus `blocked_by` parentage, resolved as in `lisa:linear-read-issue`) so S15 can classify the item. Otherwise parse the YAML spec.
228
258
  2. Resolve team ID via `mcp__linear-server__list_teams({query: <teamKey>})` if any feasibility gate will run.
229
259
  3. Run every Specification gate in order. Collect PASS / FAIL / N/A with a one-line reason.
230
260
  4. Unless the caller passed `--spec-only` (dry-run), run every Feasibility gate. Collect results.
@@ -252,6 +282,7 @@ Output is a single fenced text block. Callers parse it; do not add free-form pro
252
282
  - [PASS|FAIL|N/A] S12 Source Precedence — <one-line reason>
253
283
  - [PASS|FAIL|N/A] S13 Relationship Search — <one-line reason>
254
284
  - [PASS|FAIL|N/A] S14 Evidence manifest binding — <one-line reason>
285
+ - [PASS|FAIL|N/A] S15 Leaf-only build-ready — <one-line reason>
255
286
 
256
287
  ### Feasibility Gates (omit when --spec-only)
257
288
  - [PASS|FAIL|N/A] F1 Issue type valid in team — <one-line reason>
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: tracker-build-intake
3
- description: "Vendor-neutral wrapper for the build-queue batch scanner. Reads `tracker` from .lisa.config.json (default: jira) and dispatches to lisa:jira-build-intake (JQL/project-key queue), lisa:github-build-intake (GitHub repo queue keyed off the `status:ready` label), or lisa:linear-build-intake (Linear team queue keyed off the `status:ready` label). Counterpart to lisa:intake's PRD-side dispatchers."
3
+ description: "Vendor-neutral wrapper for the build-queue batch scanner. Reads `tracker` from .lisa.config.json (default: jira) and dispatches to lisa:jira-build-intake (JQL/project-key queue), lisa:github-build-intake (GitHub repo queue keyed off the `status:ready` label), or lisa:linear-build-intake (Linear team queue keyed off the `status:ready` label). Every vendor scanner enforces the claim-time arm of the `leaf-only-lifecycle` rule — claim leaf work units only; skip or safe-block a container with open child work (or a childless Epic/Story/Spike) that carries a stale build-ready role. Counterpart to lisa:intake's PRD-side dispatchers."
4
4
  allowed-tools: ["Skill", "Bash", "Read"]
5
5
  ---
6
6
 
@@ -20,8 +20,26 @@ See the `config-resolution` rule for configuration and dispatch table.
20
20
  - Anything else → stop and report `"Unknown tracker '<value>' in .lisa.config.json. Expected 'jira', 'github', or 'linear'."`
21
21
  3. Pass through the cycle summary verbatim.
22
22
 
23
+ ## Leaf-only claim contract (forwarded to every vendor)
24
+
25
+ This shim is dispatch only — it does not reclassify or re-gate items — but the contract it forwards is part of the build-intake API, so it is documented here once and the three vendor scanners implement it identically. Per the vendor-neutral `leaf-only-lifecycle` rule, **build intake claims only independently implementable leaf work units**:
26
+
27
+ - A **leaf work unit** (Bug, Task, Sub-task, Improvement with no open child work) is claimed and built.
28
+ - A **container** — anything with open child work, or a childless Epic/Story/Spike — that still carries a stale build-ready role is **never claimed**. The vendor scanner skips it or safe-blocks it with an idempotent lifecycle-repair comment (move the build-ready role off the parent onto its leaf children; a parent's state rolls up from its children and is never set to ready directly).
29
+
30
+ This is the claim-time arm of the rule. Its siblings are the write-time labeling (`lisa:tracker-write` → the vendor `*-write-*` skills apply build-ready to leaves only) and the validate-time S15 gate (`lisa:tracker-validate` → the vendor `*-validate-*` skills FAIL a build-ready container). All three arms cite `leaf-only-lifecycle` so no vendor drifts. Each vendor scanner implements the gate against its own hierarchy:
31
+
32
+ | Tracker | Vendor scanner | Hierarchy used to detect open child work |
33
+ |---|---|---|
34
+ | `github` | `lisa:github-build-intake` (Phase 3a) | native sub-issues (GraphQL) + body parentage |
35
+ | `jira` | `lisa:jira-build-intake` (Phase 3a) | native Epic → Story → Sub-task parentage |
36
+ | `linear` | `lisa:linear-build-intake` (Phase 3a) | native sub-issues via `parentId` + Project grouping |
37
+
38
+ The shim never needs to inspect the item itself — it forwards `$ARGUMENTS` verbatim and the resolved vendor scanner runs its Phase 3a gate before any claim.
39
+
23
40
  ## Rules
24
41
 
25
42
  - Single cycle per invocation — the vendor skill processes the current `Ready` set and exits.
26
43
  - The vendor skills run their own pre-flight checks (JIRA workflow transitions for the JIRA path; label namespace adoption for the GitHub and Linear paths) before processing items. Never bypass.
44
+ - **Leaf-only claim, every vendor.** Per the `leaf-only-lifecycle` rule, each vendor scanner claims leaf work units only and skips / safe-blocks a container (open child work, or a childless Epic/Story/Spike) carrying a stale build-ready role. This shim does not re-implement the gate — it relies on the vendor scanner's Phase 3a — but the contract is uniform across `jira`, `github`, and `linear` so behavior never drifts by tracker.
27
45
  - Never run two intake cycles concurrently against overlapping queues — the scheduling layer is responsible for serialization.
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-cdk",
3
- "version": "2.33.2",
3
+ "version": "2.35.0",
4
4
  "description": "AWS CDK-specific plugin",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-cdk",
3
- "version": "2.33.2",
3
+ "version": "2.35.0",
4
4
  "description": "AWS CDK-specific Lisa plugin.",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-expo",
3
- "version": "2.33.2",
3
+ "version": "2.35.0",
4
4
  "description": "Expo/React Native-specific skills, agents, rules, and MCP servers",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-expo",
3
- "version": "2.33.2",
3
+ "version": "2.35.0",
4
4
  "description": "Expo and React Native-specific skills, agents, rules, and MCP servers.",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-harper-fabric",
3
- "version": "2.33.2",
3
+ "version": "2.35.0",
4
4
  "description": "Harper/Fabric-specific rules for TypeScript component apps",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-harper-fabric",
3
- "version": "2.33.2",
3
+ "version": "2.35.0",
4
4
  "description": "Harper/Fabric-specific Lisa rules for TypeScript component apps.",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-nestjs",
3
- "version": "2.33.2",
3
+ "version": "2.35.0",
4
4
  "description": "NestJS-specific skills (GraphQL, TypeORM) and hooks (migration write-protection)",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-nestjs",
3
- "version": "2.33.2",
3
+ "version": "2.35.0",
4
4
  "description": "NestJS-specific skills and migration write-protection hooks.",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-openclaw",
3
- "version": "2.33.2",
3
+ "version": "2.35.0",
4
4
  "description": "Connect staff roles to Telegram or Slack via OpenClaw — facilitator/specialist hub-and-spoke routing and repo-coding topics, for Claude Code and Codex",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-openclaw",
3
- "version": "2.33.2",
3
+ "version": "2.35.0",
4
4
  "description": "Connect staff roles to Telegram or Slack via OpenClaw — facilitator/specialist hub-and-spoke routing and repo-coding topics, across Claude and Codex.",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-rails",
3
- "version": "2.33.2",
3
+ "version": "2.35.0",
4
4
  "description": "Ruby on Rails-specific hooks — RuboCop linting/formatting and ast-grep scanning on edit",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-rails",
3
- "version": "2.33.2",
3
+ "version": "2.35.0",
4
4
  "description": "Ruby on Rails-specific skills and hooks for RuboCop and ast-grep scanning on edit.",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-typescript",
3
- "version": "2.33.2",
3
+ "version": "2.35.0",
4
4
  "description": "TypeScript-specific hooks — Prettier formatting, ESLint linting, and ast-grep scanning on edit",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-typescript",
3
- "version": "2.33.2",
3
+ "version": "2.35.0",
4
4
  "description": "TypeScript-specific hooks for formatting, linting, and ast-grep scanning on edit.",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-wiki",
3
- "version": "2.33.2",
3
+ "version": "2.35.0",
4
4
  "description": "LLM Wiki — a distributable, git-native markdown knowledge base for Claude Code and Codex",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-wiki",
3
- "version": "2.33.2",
3
+ "version": "2.35.0",
4
4
  "description": "Distributable LLM Wiki kernel — ingest, query, lint, and maintain a git-native markdown knowledge base across Claude and Codex.",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -0,0 +1,6 @@
1
+ ---
2
+ description: "Scaffold, repair, verify, or upgrade the project's LLM Wiki from its config. Alias for lisa-wiki setup in the Lisa setup command family."
3
+ argument-hint: "[--upgrade] [--with-ci]"
4
+ ---
5
+
6
+ Use the lisa-wiki-setup skill to bring the wiki into conformance from wiki/lisa-wiki.config.json: validate config, ask purpose + README mode, scaffold the canonical structure, render the contract snapshot (stamping kernelVersion), seed the staff roster, then verify with /doctor. $ARGUMENTS
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: jira-build-intake
3
- description: "Symmetric counterpart to notion-prd-intake on the JIRA side. Scans a JIRA project (or JQL filter) for tickets in the configured `ready` status, claims each by transitioning to the configured `claimed` status, runs the implementation/build flow via jira-agent, and transitions to the configured `done` status on completion. The `ready` status is the human-flipped signal that a TODO ticket is truly ready for development — mirroring how Notion PRDs work product Draft → Ready → (us) In Review → Blocked|Ticketed."
3
+ description: "Symmetric counterpart to notion-prd-intake on the JIRA side. Scans a JIRA project (or JQL filter) for tickets in the configured `ready` status, claims each by transitioning to the configured `claimed` status, runs the implementation/build flow via jira-agent, and transitions to the configured `done` status on completion. 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 status is skipped or safe-blocked with a lifecycle-repair comment, never claimed. The `ready` status is the human-flipped signal that a TODO ticket is truly ready for development — mirroring how Notion PRDs work product Draft → Ready → (us) In Review → Blocked|Ticketed."
4
4
  allowed-tools: ["Skill", "Bash"]
5
5
  ---
6
6
 
@@ -118,7 +118,50 @@ If empty, report `"No tickets with Status=$READY. Nothing to do."` and exit. Thi
118
118
 
119
119
  ### Phase 3 — Process each ready ticket (serial)
120
120
 
121
- #### 3a. Claim
121
+ #### 3a. Leaf-only claim gate (skip / safe-block containers)
122
+
123
+ 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 status (e.g. `Ready` applied before this rule existed, or hand-applied to an Epic/Story) 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:jira-write-ticket` and the validate-time S15 gate in `lisa:jira-validate-ticket`; all three cite the same rule so the classification never drifts. **Never silently implement a container.**
124
+
125
+ Run this gate **before** the claim transition, for every candidate ticket. Do NOT transition, comment "Claimed", or invoke `lisa:jira-agent` for a ticket that fails the gate.
126
+
127
+ **Resolve container vs. leaf — structural first, then nominal.** Per `leaf-only-lifecycle` the classification is structural: a ticket is a **container** if it has **open** child work, whatever its declared type; otherwise the **issue type** decides. Resolve child work using the same hierarchy `lisa:jira-read-ticket` uses — JIRA's native Epic → Story → Sub-task parentage (Epic link / parent field for Stories under an Epic, and the subtask relationship for Sub-tasks under a Story/Task). Issue links (`blocks` / `is blocked by`) express cross-item dependencies and are **not** parentage — do not count them as children.
128
+
129
+ Fetch the ticket's children via `lisa:atlassian-access` `operation: search-issues` with a JQL that resolves both subtasks and Epic-linked Stories, then count those still open (not in a resolved/Done status):
130
+
131
+ ```bash
132
+ # Children of <TICKET>: native subtasks plus, for an Epic, its linked Stories.
133
+ # (parent = <TICKET>) covers Sub-tasks and child issues; ("Epic Link" = <TICKET>)
134
+ # covers Stories under an Epic on JIRA instances that expose the Epic Link field.
135
+ CHILDREN_JQL='(parent = "<TICKET>" OR "Epic Link" = "<TICKET>")'
136
+ # Count children whose status is NOT a resolved/terminal one. A parent whose
137
+ # children are all Done is no longer holding open work and rolls up via
138
+ # leaf-only-lifecycle's rollup, not here.
139
+ OPEN_CHILDREN_JQL="${CHILDREN_JQL} AND statusCategory != Done"
140
+ ```
141
+
142
+ Invoke `lisa:atlassian-access` `operation: search-issues jql: "<OPEN_CHILDREN_JQL>"` and let `OPEN_CHILDREN` be the count of returned issues (0 if none). If the JQL cannot resolve the `Epic Link` field on this instance (older JIRA / team-managed projects expose parentage differently), fall back to the parentage `lisa:jira-read-ticket` derives and treat the ticket as a container if any derived child is open. Note "Epic Link unavailable — parentage derived" so the operator knows how children were resolved.
143
+
144
+ Classify and act (first match wins). The issue type comes from the ticket's `issuetype` field (`Epic`, `Story`, `Spike`, `Bug`, `Task`, `Sub-task`, `Improvement`):
145
+
146
+ | Condition | Class | Action |
147
+ |---|---|---|
148
+ | `OPEN_CHILDREN > 0` (open child work, any type) | **Container** | **Skip / safe-block — do NOT claim** |
149
+ | no open children AND type ∈ {Epic, Story, Spike} | **Childless container-type** | **Skip / safe-block — do NOT claim** |
150
+ | no open children AND type ∈ {Bug, Task, Sub-task, Improvement} (or no recognized type) | **Leaf work unit** | **Proceed to 3b claim** |
151
+
152
+ 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.
153
+
154
+ **Safe-block (default action for a flagged container).** Leave the build-ready status in place (don't silently transition it away — that hides the lifecycle error), post a single lifecycle-repair comment, and record the ticket under "Skipped (container)" in the summary. Do NOT transition to `$CLAIMED`. Keep the comment idempotent — skip posting if an identical `[claude-build-intake]` lifecycle-repair comment already exists on the ticket, so a re-entrant cycle doesn't spam it.
155
+
156
+ Post via `lisa:atlassian-access` `operation: comment key: <TICKET> body: "<message>"` with:
157
+
158
+ ```text
159
+ [claude-build-intake] Not claimed: this ticket carries the build-ready status ($READY) but is a container with open child work (or a childless Epic/Story/Spike), which violates the leaf-only-lifecycle rule. Build-ready (status:ready) is leaf-only per leaf-only-lifecycle — an agent claims and implements leaves, never a container. Repair: move $READY off this parent onto its leaf children (or, for a childless Epic/Story/Spike, decompose it into leaf children or reclassify it to a leaf type). A parent's lifecycle state rolls up from its children and is never set to ready directly.
160
+ ```
161
+
162
+ This gate never blocks a legitimate flat Task/Bug: those have no open children and a leaf type, so they fall straight through to the claim in 3b.
163
+
164
+ #### 3b. Claim
122
165
 
123
166
  Transition the ticket from `$READY` to `$CLAIMED` by invoking `lisa:atlassian-access` `operation: transition key: <TICKET> to: "$CLAIMED"`.
124
167
  - Post a `[claude-build-intake]` comment via `lisa:atlassian-access` `operation: comment key: <TICKET> body: "Claimed by Claude. Starting build."`
@@ -126,7 +169,7 @@ Transition the ticket from `$READY` to `$CLAIMED` by invoking `lisa:atlassian-ac
126
169
 
127
170
  If the transition fails (permission, missing transition, race), log under "Errors" in the cycle summary and skip this ticket. **Do not invoke the build flow on a ticket you didn't successfully claim.**
128
171
 
129
- #### 3b. Run the build flow
172
+ #### 3c. Run the build flow
130
173
 
131
174
  Invoke the `lisa:jira-agent` (existing per-ticket lifecycle agent) with the ticket key. `lisa:jira-agent` owns:
132
175
  - Reading the full ticket graph (`lisa:jira-read-ticket`)
@@ -142,7 +185,7 @@ Wait for `lisa:jira-agent` to return. Capture its outcome:
142
185
  - **Blocked by ticket-triage ambiguities** — `lisa:jira-agent` posts findings and stops. The ticket stays in `$CLAIMED`. Surface to human; do not auto-transition. Record under "Errors" with reason `"Triage found ambiguities — see comments on <ticket-key>"`.
143
186
  - **Errored** — exception, missing config, etc. Leave the ticket in `$CLAIMED` for human investigation. Record under "Errors" with the exception summary.
144
187
 
145
- #### 3c. Transition to $DONE (only on Success)
188
+ #### 3d. Transition to $DONE (only on Success)
146
189
 
147
190
  If `lisa:jira-agent` returned Success:
148
191
  1. Resolve `$DONE` for this ticket's PR base branch using the Workflow resolution algorithm above. If env can't be resolved and `done` is env-keyed, record an Error and skip this transition — never guess.
@@ -151,7 +194,7 @@ If `lisa:jira-agent` returned Success:
151
194
 
152
195
  For any non-Success outcome, do NOT transition. The ticket sits in `$CLAIMED` (or wherever `lisa:jira-agent` left it for the Blocked case) — the cycle's job is done; humans take it from there.
153
196
 
154
- #### 3d. Continue
197
+ #### 3e. Continue
155
198
 
156
199
  Move to the next ready ticket. One ticket failing does not stop others.
157
200
 
@@ -167,6 +210,8 @@ Cycle completed: <ISO timestamp>
167
210
  Tickets processed: <n>
168
211
  - $DONE (build complete, PR ready): <n>
169
212
  - <ticket-key> <summary> → PR <URL>
213
+ - Skipped (container — leaf-only-lifecycle): <n>
214
+ - <ticket-key> <summary> — build-ready on a parent with open child work; lifecycle-repair comment posted
170
215
  - Blocked (pre-flight verify failed): <n>
171
216
  - <ticket-key> <summary> — see ticket comments
172
217
  - Held (triage found ambiguities): <n>
@@ -179,6 +224,7 @@ Total PRs opened: <n>
179
224
 
180
225
  ## Idempotency & safety
181
226
 
227
+ - **Leaf-only claim gate runs first**: Phase 3a classifies each candidate before any claim; a container with open child work (or a childless Epic/Story/Spike) is skipped/safe-blocked, never claimed (the `leaf-only-lifecycle` rule's claim-time arm). The safe-block comment is idempotent — a re-entrant cycle does not re-post it.
182
228
  - **Claim-first ordering**: `$CLAIMED` set BEFORE `lisa:jira-agent` invocation — no double-pickup.
183
229
  - **No writes outside the lifecycle**: this skill only transitions `$READY → $CLAIMED` and `$CLAIMED → $DONE`. Every other status change is owned by `lisa:jira-agent` (which suggests transitions but only auto-transitions on the verify-FAIL path).
184
230
  - **Failure isolation**: per-ticket exceptions caught and recorded; the cycle continues.
@@ -211,6 +257,7 @@ If a ready-equivalent status does not exist in the JIRA project's workflow, this
211
257
 
212
258
  ## Rules
213
259
 
260
+ - **Claim leaves only.** Per the `leaf-only-lifecycle` rule, never claim a container — a ticket with open child work, or a childless Epic/Story/Spike — even if it carries the build-ready status. Skip or safe-block it (Phase 3a); never silently implement a container.
214
261
  - Never transition a ticket the cycle didn't claim. The `$CLAIMED` transition is the signature of cycle ownership.
215
262
  - Never bypass `lisa:jira-agent` to do build work directly. `lisa:jira-agent` owns the per-ticket lifecycle (read, verify, triage, route, sync, evidence). This skill is the dispatcher, not the builder.
216
263
  - Never auto-transition past `$DONE`. Downstream statuses are owned by QA / product / a future verification-intake skill — not this one.
@@ -60,9 +60,11 @@ authenticated_surface: true # → requires Sign-in Required
60
60
  artifacts_attached: true # → requires Source Precedence section
61
61
  links: [{ key: "PROJ-99", type: "is blocked by" }] # known issue links (may be empty)
62
62
  remote_links: [{ url: "https://github.com/...", title: "PR #42" }]
63
+ build_ready: true # caller asserts the build-ready role (status:ready) is/would be applied — see S15
64
+ child_refs: ["PROJ-601", "PROJ-602"] # known child work (sub-tasks / "is blocked by" parentage) — see S15
63
65
  ```
64
66
 
65
- If the caller passes only a ticket key, fetch the ticket via `lisa:atlassian-access` `operation: read-ticket key: <KEY>`, derive the same fields from the fetched data, then run gates.
67
+ If the caller passes only a ticket key, fetch the ticket via `lisa:atlassian-access` `operation: read-ticket key: <KEY>`, derive the same fields from the fetched data — including `build_ready` (label set contains `status:ready`) and `child_refs` (sub-tasks plus `is blocked by` parentage, resolved as in `lisa:jira-read-ticket`) so S15 can classify the ticket — then run gates.
66
68
 
67
69
  ## Gates
68
70
 
@@ -86,6 +88,7 @@ Each gate is tagged with a fixed `category` and a `product_relevant` boolean. Ca
86
88
  | S12 Source Precedence | `design-ux` | true |
87
89
  | S13 Relationship Search | `dependency` | true |
88
90
  | S14 Evidence manifest binding (leaf work units) | `acceptance-criteria` | true |
91
+ | S15 Leaf-only build-ready | `structural` | false |
89
92
  | F1 Issue type valid in project | `structural` | false |
90
93
  | F2 Epic parent exists and is an Epic | `structural` | false |
91
94
  | F3 Linked tickets exist | `structural` | false |
@@ -199,6 +202,33 @@ FAIL when the Validation Journey is present but declares zero `[EVIDENCE: name]`
199
202
 
200
203
  This gate depends on S11. It is `N/A` for Epic / 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:jira-add-journey`.
201
204
 
205
+ #### S15 — Leaf-only build-ready
206
+
207
+ Enforces the build-side of the vendor-neutral `leaf-only-lifecycle` rule: **only a leaf work unit may carry the build-ready role.** This is the symmetric write-side guard for the JIRA validator — a stale or hand-applied `status:ready` label on a container is a lifecycle error and must FAIL here, regardless of how the ticket was produced. (Mirrors the "Build-ready label is leaf-only" rule that `lisa:jira-write-ticket` applies at write time.)
208
+
209
+ **When the gate applies.** Run S15 whenever the ticket is build-ready — i.e. `build_ready = true`, or the spec/live labels include `status:ready`. If the ticket is not build-ready, S15 is `N/A` (nothing claims a non-ready ticket, so the invariant is vacuous).
210
+
211
+ **Resolve container vs. leaf — structural first, then nominal.** Per `leaf-only-lifecycle` the classification is structural: an item is a **container** if it has child work, whatever its declared type; otherwise the **issue type** decides. Determine child work from (in order) `child_refs`, native sub-tasks, and `is blocked by` / parent references — the same hierarchy resolution `lisa:jira-read-ticket` uses. When validating a live key, query sub-tasks alongside the ticket fetch.
212
+
213
+ Apply this decision and FAIL the two invariant-violating cases:
214
+
215
+ 1. **Container with child work + build-ready** — `issue_type ∈ {Epic, Story, Spike}` OR child work is present (any type that has children), AND build-ready. FAIL. A parent organizes work; it is never claimed and implemented directly. Its lifecycle state rolls up from its children.
216
+ 2. **Childless container-type + build-ready** — `issue_type ∈ {Epic, Story, Spike}` with **no** child work, AND build-ready. Still FAIL: these types are coordination containers by design, and an empty one is an incomplete decomposition, not an implementable unit (the childless-parent exception in `leaf-only-lifecycle` does **not** promote an Epic/Story/Spike to build-ready).
217
+
218
+ PASS (the childless-parent exception) when the ticket is build-ready and is a **leaf work unit**: `issue_type ∈ {Bug, Task, Sub-task, Improvement}` AND has **no** child work. A flat Task or Bug with no sub-tasks is a valid build-ready leaf and must not be stranded.
219
+
220
+ | issue_type | has child work | build-ready | S15 |
221
+ |---|---|---|---|
222
+ | Bug / Task / Sub-task / Improvement | no | yes | **PASS** (leaf) |
223
+ | Bug / Task / Sub-task / Improvement | yes | yes | **FAIL** (structurally a container) |
224
+ | Epic / Story / Spike | yes | yes | **FAIL** (container with children) |
225
+ | Epic / Story / Spike | no | yes | **FAIL** (childless container-type, exception does not apply) |
226
+ | any | any | no | **N/A** (not build-ready) |
227
+
228
+ Remediation: `"Build-ready (status:ready) is leaf-only per leaf-only-lifecycle. Move status:ready off this container onto its leaf children (or, for a childless Epic/Story/Spike, decompose it into leaf children or reclassify it to a leaf type); a parent's lifecycle state rolls up from its children and is never set to ready directly."`
229
+
230
+ `product_relevant: false` — a build-ready container is a lifecycle/decomposition error for the caller to repair, not a product question.
231
+
202
232
  ### Feasibility Gates (require JIRA lookups; skip in dry-run if requested)
203
233
 
204
234
  #### F1 — Issue type valid in project
@@ -219,7 +249,7 @@ Use the same project-issue-type-metadata lookup from F1 (via `lisa:atlassian-acc
219
249
 
220
250
  ## Execution
221
251
 
222
- 1. Parse `$ARGUMENTS`. If it's a ticket key, fetch the ticket via `lisa:atlassian-access` `operation: read-ticket` and derive the spec from the fetched fields. Otherwise parse the YAML spec.
252
+ 1. Parse `$ARGUMENTS`. If it's a ticket key, fetch the ticket via `lisa:atlassian-access` `operation: read-ticket` and derive the spec from the fetched fields — including `build_ready` (label set contains `status:ready`) and `child_refs` (sub-tasks plus `is blocked by` parentage, resolved as in `lisa:jira-read-ticket`) so S15 can classify the ticket. Otherwise parse the YAML spec.
223
253
  2. If any feasibility gate will run, invoke `lisa:atlassian-access` `operation: list-sites` once to confirm the configured site is reachable (it enforces connection match against `.lisa.config.json`).
224
254
  3. Run every Specification gate in order. Collect PASS / FAIL / N/A with a one-line reason.
225
255
  4. Unless the caller passed `--spec-only` (dry-run), run every Feasibility gate. Collect results.
@@ -247,6 +277,7 @@ Output is a single fenced text block. Callers parse it; do not add free-form pro
247
277
  - [PASS|FAIL|N/A] S12 Source Precedence — <one-line reason>
248
278
  - [PASS|FAIL|N/A] S13 Relationship Search — <one-line reason>
249
279
  - [PASS|FAIL|N/A] S14 Evidence manifest binding — <one-line reason>
280
+ - [PASS|FAIL|N/A] S15 Leaf-only build-ready — <one-line reason>
250
281
 
251
282
  ### Feasibility Gates (omit this section when --spec-only)
252
283
  - [PASS|FAIL|N/A] F1 Issue type valid in project — <one-line reason>
@@ -271,7 +302,7 @@ The verdict is `PASS` if and only if every applicable gate is `PASS`. Any `FAIL`
271
302
 
272
303
  ### Failure-detail fields
273
304
 
274
- - **gate**: the gate ID (`S1`–`S14`, `F1`–`F4`).
305
+ - **gate**: the gate ID (`S1`–`S15`, `F1`–`F4`).
275
306
  - **category**: the gate's fixed category from the table above. Callers use this to label or filter comments — `product-clarity`, `acceptance-criteria`, `design-ux`, `scope`, `dependency`, `data`, `technical`, or `structural`.
276
307
  - **product_relevant**: matches the gate's table entry. `false` means the failure is an internal data-quality problem (e.g., the agent built a malformed spec, an issue type is invalid in the project) and the caller should fix it without bothering the product team. `true` means the PRD needs product input to resolve.
277
308
  - **what**: plain-language description of the issue. No gate IDs, no JIRA jargon, no engineering shorthand. A product owner reading this on a Notion comment should understand what is unclear and why.
@@ -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 each by relabeling to the configured `claimed` label, runs the implementation/build flow via lisa:linear-agent, and relabels to the configured `done` label on completion. 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."
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 each by relabeling to the configured `claimed` label, runs the implementation/build flow via lisa:linear-agent, and relabels to the configured `done` label on completion. 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
 
@@ -128,7 +128,48 @@ If empty, report `"No Linear Issues labeled $READY. Nothing to do."` and exit. C
128
128
 
129
129
  ### Phase 3 — Process each ready Issue (serial)
130
130
 
131
- #### 3a. Claim
131
+ #### 3a. Leaf-only claim gate (skip / safe-block containers)
132
+
133
+ 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
+
135
+ Run this gate **before** the claim relabel, for every candidate Issue. Do NOT relabel, comment "Claimed", or invoke `lisa:linear-agent` for an Issue that fails the gate.
136
+
137
+ **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
+
139
+ Fetch the Issue's sub-issues via `mcp__linear-server__get_issue` (which returns the children) or `mcp__linear-server__list_issues({parentId: <issueId>})`, then count those still open (Linear `state.type` not in the completed/canceled set):
140
+
141
+ ```text
142
+ # Children of <issueId>: native sub-issues via parentId.
143
+ # Count children whose Linear state.type is NOT terminal ("completed" / "canceled").
144
+ # A parent whose children are all completed is no longer holding open work and
145
+ # rolls up via leaf-only-lifecycle's rollup, not here.
146
+ OPEN_CHILDREN = count(list_issues({parentId: <issueId>})
147
+ where state.type not in {"completed", "canceled"})
148
+ ```
149
+
150
+ For a Project-level parent (an Issue that itself anchors a `projectId` grouping rather than a `parentId` tree), resolve membership the same way `lisa:linear-read-issue` does and treat the parent as a container if any grouped Issue is still open. If sub-issue resolution is unavailable, fall back to the parentage `lisa:linear-read-issue` derives and treat the Issue as a container if any derived child is open. Note "sub-issues unavailable — parentage derived" so the operator knows how children were resolved.
151
+
152
+ Classify and act (first match wins). The type comes from the Issue's `type:` label (`type:Epic`, `type:Story`, `type:Spike`, `type:Bug`, `type:Task`, `type:Sub-task`, `type:Improvement`):
153
+
154
+ | Condition | Class | Action |
155
+ |---|---|---|
156
+ | `OPEN_CHILDREN > 0` (open child work, any type) | **Container** | **Skip / safe-block — do NOT claim** |
157
+ | no open children AND type ∈ {Epic, Story, Spike} | **Childless container-type** | **Skip / safe-block — do NOT claim** |
158
+ | no open children AND type ∈ {Bug, Task, Sub-task, Improvement} (or no `type:` label) | **Leaf work unit** | **Proceed to 3b claim** |
159
+
160
+ 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
+
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, and record the Issue under "Skipped (container)" in the summary. 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
+
164
+ Post via `mcp__linear-server__save_comment` with:
165
+
166
+ ```text
167
+ [claude-build-intake] Not claimed: this Issue carries the build-ready label ($READY) but is a container with open child work (or a childless Epic/Story/Spike), which violates the leaf-only-lifecycle rule. Build-ready (status:ready) is leaf-only per leaf-only-lifecycle — an agent claims and implements leaves, never a container. Repair: move $READY off this parent onto its leaf children (or, for a childless Epic/Story/Spike, decompose it into leaf children or reclassify it to a leaf type). A parent's lifecycle state rolls up from its children and is never set to ready directly.
168
+ ```
169
+
170
+ This gate never blocks a legitimate flat Task/Bug: those have no open children and a leaf `type:`, so they fall straight through to the claim in 3b.
171
+
172
+ #### 3b. Claim
132
173
 
133
174
  Update labels via `mcp__linear-server__save_issue`: remove `$READY`, add `$CLAIMED`. Resolve label IDs via `list_issue_labels` (create `$CLAIMED` if missing).
134
175
 
@@ -138,7 +179,7 @@ This is the idempotency lock — a re-entrant cycle's `label: $READY` filter wil
138
179
 
139
180
  If the relabel fails (permission, race), record under "Errors" and skip. **Do not invoke the build flow on an Issue you didn't successfully claim.**
140
181
 
141
- #### 3b. Run the build flow
182
+ #### 3c. Run the build flow
142
183
 
143
184
  Invoke `lisa:linear-agent` (per-Issue lifecycle agent) with the Issue identifier. `lisa:linear-agent` owns:
144
185
  - Reading the full Issue graph (`lisa:linear-read-issue`)
@@ -154,7 +195,7 @@ Wait for the agent to return. Capture its outcome:
154
195
  - **Blocked by ticket-triage ambiguities** — agent posts findings and stops. The Issue stays at `$CLAIMED`. Surface to human; do not auto-transition. Record under "Errors".
155
196
  - **Errored** — exception, missing config, etc. Leave at `$CLAIMED`. Record with exception summary.
156
197
 
157
- #### 3c. Relabel to $DONE (only on Success)
198
+ #### 3d. Relabel to $DONE (only on Success)
158
199
 
159
200
  If `lisa:linear-agent` returned Success:
160
201
  1. Resolve `$DONE` for this issue's PR base branch using the Workflow resolution algorithm above. If env can't be resolved and `done` is env-keyed, record an Error and skip this transition — never guess.
@@ -163,7 +204,7 @@ If `lisa:linear-agent` returned Success:
163
204
 
164
205
  For any non-Success outcome, do NOT transition. The Issue sits where the agent left it — humans take it from there.
165
206
 
166
- #### 3d. Continue
207
+ #### 3e. Continue
167
208
 
168
209
  Move to the next ready Issue. One Issue failing does not stop others.
169
210
 
@@ -179,6 +220,8 @@ Cycle completed: <ISO timestamp>
179
220
  Issues processed: <n>
180
221
  - $DONE (build complete, PR ready): <n>
181
222
  - <ID> <title> → PR <URL>
223
+ - Skipped (container — leaf-only-lifecycle): <n>
224
+ - <ID> <title> — build-ready on a parent with open child work; lifecycle-repair comment posted
182
225
  - status:blocked (pre-flight verify failed): <n>
183
226
  - <ID> <title> — see Issue comments
184
227
  - Held (triage found ambiguities): <n>
@@ -191,6 +234,7 @@ Total PRs opened: <n>
191
234
 
192
235
  ## Idempotency & safety
193
236
 
237
+ - **Leaf-only claim gate runs first**: Phase 3a classifies each candidate before any claim; a container with open child work (or a childless Epic/Story/Spike) is skipped/safe-blocked, never claimed (the `leaf-only-lifecycle` rule's claim-time arm). The safe-block comment is idempotent — a re-entrant cycle does not re-post it.
194
238
  - **Claim-first ordering**: `$CLAIMED` set BEFORE agent invocation — no double-pickup.
195
239
  - **No writes outside the lifecycle**: this skill only adds/removes `$READY`, `$CLAIMED`, `$DONE`. Every other label change (and the native state) is owned by the agent or `lisa:linear-evidence`.
196
240
  - **Failure isolation**: per-Issue exceptions caught and recorded; the cycle continues.
@@ -210,6 +254,7 @@ If the team hasn't adopted these labels, the first run exits with an adoption hi
210
254
 
211
255
  ## Rules
212
256
 
257
+ - **Claim leaves only.** Per the `leaf-only-lifecycle` rule, never claim a container — an Issue with open child work, or a childless Epic/Story/Spike — even if it carries the build-ready label. Skip or safe-block it (Phase 3a); never silently implement a container.
213
258
  - Never relabel an Issue the cycle didn't claim. The `$CLAIMED` transition is the signature of cycle ownership.
214
259
  - Never bypass `lisa:linear-agent` to do build work directly. The agent owns the per-Issue lifecycle.
215
260
  - Never auto-transition past `$DONE`. Downstream labels are owned by QA / product / a future verification-intake skill.
@@ -61,9 +61,11 @@ authenticated_surface: true # → requires Sign-in Required
61
61
  artifacts_attached: true # → requires Source Precedence section
62
62
  relations: [{ id: "ENG-99", type: "blocked_by" }] # known issue relations (may be empty)
63
63
  remote_links: [{ url: "https://github.com/...", title: "PR #42" }]
64
+ build_ready: true # caller asserts the build-ready role (status:ready) is/would be applied — see S15
65
+ child_refs: ["ENG-601", "ENG-602"] # known child work (sub-issues / project-member issues / blocked_by parentage) — see S15
64
66
  ```
65
67
 
66
- If the caller passes only an identifier, fetch the item via `mcp__linear-server__get_issue` (Issue) or `mcp__linear-server__get_project` (Project), derive the same fields from the fetched data, then run gates.
68
+ If the caller passes only an identifier, fetch the item via `mcp__linear-server__get_issue` (Issue) or `mcp__linear-server__get_project` (Project), derive the same fields from the fetched data — including `build_ready` (label set contains `status:ready`) and `child_refs` (sub-issues, project-member issues, plus `blocked_by` parentage, resolved as in `lisa:linear-read-issue`) so S15 can classify the item — then run gates.
67
69
 
68
70
  ## Gates
69
71
 
@@ -87,6 +89,7 @@ Each gate is tagged with a fixed `category` and a `product_relevant` boolean. Ca
87
89
  | S12 Source Precedence | `design-ux` | true |
88
90
  | S13 Relationship Search | `dependency` | true |
89
91
  | S14 Evidence manifest binding (leaf work units) | `acceptance-criteria` | true |
92
+ | S15 Leaf-only build-ready | `structural` | false |
90
93
  | F1 Issue type valid in team | `structural` | false |
91
94
  | F2 Project parent exists and is in same team | `structural` | false |
92
95
  | F3 Linked items exist | `structural` | false |
@@ -200,6 +203,33 @@ FAIL when the Validation Journey is present but declares zero `[EVIDENCE: name]`
200
203
 
201
204
  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
205
 
206
+ #### S15 — Leaf-only build-ready
207
+
208
+ Enforces the build-side of the vendor-neutral `leaf-only-lifecycle` rule: **only a leaf work unit may carry the build-ready role.** This is the symmetric write-side guard for the Linear validator — a stale or hand-applied `status:ready` label on a container is a lifecycle error and must FAIL here, regardless of how the item was produced. (Mirrors the "Build-ready label is leaf-only" rule that `lisa:linear-write-issue` applies at write time.)
209
+
210
+ **When the gate applies.** Run S15 whenever the item is build-ready — i.e. `build_ready = true`, or the spec/live labels include `status:ready`. If the item is not build-ready, S15 is `N/A` (nothing claims a non-ready item, so the invariant is vacuous).
211
+
212
+ **Resolve container vs. leaf — structural first, then nominal.** Per `leaf-only-lifecycle` the classification is structural: an item is a **container** if it has child work, whatever its declared type; otherwise the **issue type** decides. Determine child work from (in order) `child_refs`, native sub-issues, project-member issues (an Epic is modeled as a Linear Project), and `blocked_by` / parent references — the same hierarchy resolution `lisa:linear-read-issue` uses. When validating a live identifier, query sub-issues / project members alongside the item fetch.
213
+
214
+ Apply this decision and FAIL the two invariant-violating cases:
215
+
216
+ 1. **Container with child work + build-ready** — `issue_type ∈ {Epic, Story, Spike}` OR child work is present (any type that has children), AND build-ready. FAIL. A parent organizes work; it is never claimed and implemented directly. Its lifecycle state rolls up from its children.
217
+ 2. **Childless container-type + build-ready** — `issue_type ∈ {Epic, Story, Spike}` with **no** child work, AND build-ready. Still FAIL: these types are coordination containers by design, and an empty one is an incomplete decomposition, not an implementable unit (the childless-parent exception in `leaf-only-lifecycle` does **not** promote an Epic/Story/Spike to build-ready).
218
+
219
+ PASS (the childless-parent exception) when the item is build-ready and is a **leaf work unit**: `issue_type ∈ {Bug, Task, Sub-task, Improvement}` AND has **no** child work. A flat Task or Bug with no sub-issues is a valid build-ready leaf and must not be stranded.
220
+
221
+ | issue_type | has child work | build-ready | S15 |
222
+ |---|---|---|---|
223
+ | Bug / Task / Sub-task / Improvement | no | yes | **PASS** (leaf) |
224
+ | Bug / Task / Sub-task / Improvement | yes | yes | **FAIL** (structurally a container) |
225
+ | Epic / Story / Spike | yes | yes | **FAIL** (container with children) |
226
+ | Epic / Story / Spike | no | yes | **FAIL** (childless container-type, exception does not apply) |
227
+ | any | any | no | **N/A** (not build-ready) |
228
+
229
+ Remediation: `"Build-ready (status:ready) is leaf-only per leaf-only-lifecycle. Move status:ready off this container onto its leaf children (or, for a childless Epic/Story/Spike, decompose it into leaf children or reclassify it to a leaf type); a parent's lifecycle state rolls up from its children and is never set to ready directly."`
230
+
231
+ `product_relevant: false` — a build-ready container is a lifecycle/decomposition error for the caller to repair, not a product question.
232
+
203
233
  ### Feasibility Gates (require Linear lookups; skip in dry-run if requested)
204
234
 
205
235
  #### F1 — Issue type valid in team
@@ -224,7 +254,7 @@ For each label referenced (`status:*`, `component:<name>`, `prd-*`), confirm via
224
254
 
225
255
  ## Execution
226
256
 
227
- 1. Parse `$ARGUMENTS`. If it's an identifier, fetch the item and derive the spec from the fetched fields. Otherwise parse the YAML spec.
257
+ 1. Parse `$ARGUMENTS`. If it's an identifier, fetch the item and derive the spec from the fetched fields — including `build_ready` (label set contains `status:ready`) and `child_refs` (sub-issues, project-member issues, plus `blocked_by` parentage, resolved as in `lisa:linear-read-issue`) so S15 can classify the item. Otherwise parse the YAML spec.
228
258
  2. Resolve team ID via `mcp__linear-server__list_teams({query: <teamKey>})` if any feasibility gate will run.
229
259
  3. Run every Specification gate in order. Collect PASS / FAIL / N/A with a one-line reason.
230
260
  4. Unless the caller passed `--spec-only` (dry-run), run every Feasibility gate. Collect results.
@@ -252,6 +282,7 @@ Output is a single fenced text block. Callers parse it; do not add free-form pro
252
282
  - [PASS|FAIL|N/A] S12 Source Precedence — <one-line reason>
253
283
  - [PASS|FAIL|N/A] S13 Relationship Search — <one-line reason>
254
284
  - [PASS|FAIL|N/A] S14 Evidence manifest binding — <one-line reason>
285
+ - [PASS|FAIL|N/A] S15 Leaf-only build-ready — <one-line reason>
255
286
 
256
287
  ### Feasibility Gates (omit when --spec-only)
257
288
  - [PASS|FAIL|N/A] F1 Issue type valid in team — <one-line reason>
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: tracker-build-intake
3
- description: "Vendor-neutral wrapper for the build-queue batch scanner. Reads `tracker` from .lisa.config.json (default: jira) and dispatches to lisa:jira-build-intake (JQL/project-key queue), lisa:github-build-intake (GitHub repo queue keyed off the `status:ready` label), or lisa:linear-build-intake (Linear team queue keyed off the `status:ready` label). Counterpart to lisa:intake's PRD-side dispatchers."
3
+ description: "Vendor-neutral wrapper for the build-queue batch scanner. Reads `tracker` from .lisa.config.json (default: jira) and dispatches to lisa:jira-build-intake (JQL/project-key queue), lisa:github-build-intake (GitHub repo queue keyed off the `status:ready` label), or lisa:linear-build-intake (Linear team queue keyed off the `status:ready` label). Every vendor scanner enforces the claim-time arm of the `leaf-only-lifecycle` rule — claim leaf work units only; skip or safe-block a container with open child work (or a childless Epic/Story/Spike) that carries a stale build-ready role. Counterpart to lisa:intake's PRD-side dispatchers."
4
4
  allowed-tools: ["Skill", "Bash", "Read"]
5
5
  ---
6
6
 
@@ -20,8 +20,26 @@ See the `config-resolution` rule for configuration and dispatch table.
20
20
  - Anything else → stop and report `"Unknown tracker '<value>' in .lisa.config.json. Expected 'jira', 'github', or 'linear'."`
21
21
  3. Pass through the cycle summary verbatim.
22
22
 
23
+ ## Leaf-only claim contract (forwarded to every vendor)
24
+
25
+ This shim is dispatch only — it does not reclassify or re-gate items — but the contract it forwards is part of the build-intake API, so it is documented here once and the three vendor scanners implement it identically. Per the vendor-neutral `leaf-only-lifecycle` rule, **build intake claims only independently implementable leaf work units**:
26
+
27
+ - A **leaf work unit** (Bug, Task, Sub-task, Improvement with no open child work) is claimed and built.
28
+ - A **container** — anything with open child work, or a childless Epic/Story/Spike — that still carries a stale build-ready role is **never claimed**. The vendor scanner skips it or safe-blocks it with an idempotent lifecycle-repair comment (move the build-ready role off the parent onto its leaf children; a parent's state rolls up from its children and is never set to ready directly).
29
+
30
+ This is the claim-time arm of the rule. Its siblings are the write-time labeling (`lisa:tracker-write` → the vendor `*-write-*` skills apply build-ready to leaves only) and the validate-time S15 gate (`lisa:tracker-validate` → the vendor `*-validate-*` skills FAIL a build-ready container). All three arms cite `leaf-only-lifecycle` so no vendor drifts. Each vendor scanner implements the gate against its own hierarchy:
31
+
32
+ | Tracker | Vendor scanner | Hierarchy used to detect open child work |
33
+ |---|---|---|
34
+ | `github` | `lisa:github-build-intake` (Phase 3a) | native sub-issues (GraphQL) + body parentage |
35
+ | `jira` | `lisa:jira-build-intake` (Phase 3a) | native Epic → Story → Sub-task parentage |
36
+ | `linear` | `lisa:linear-build-intake` (Phase 3a) | native sub-issues via `parentId` + Project grouping |
37
+
38
+ The shim never needs to inspect the item itself — it forwards `$ARGUMENTS` verbatim and the resolved vendor scanner runs its Phase 3a gate before any claim.
39
+
23
40
  ## Rules
24
41
 
25
42
  - Single cycle per invocation — the vendor skill processes the current `Ready` set and exits.
26
43
  - The vendor skills run their own pre-flight checks (JIRA workflow transitions for the JIRA path; label namespace adoption for the GitHub and Linear paths) before processing items. Never bypass.
44
+ - **Leaf-only claim, every vendor.** Per the `leaf-only-lifecycle` rule, each vendor scanner claims leaf work units only and skips / safe-blocks a container (open child work, or a childless Epic/Story/Spike) carrying a stale build-ready role. This shim does not re-implement the gate — it relies on the vendor scanner's Phase 3a — but the contract is uniform across `jira`, `github`, and `linear` so behavior never drifts by tracker.
27
45
  - Never run two intake cycles concurrently against overlapping queues — the scheduling layer is responsible for serialization.
@@ -0,0 +1,6 @@
1
+ ---
2
+ description: "Scaffold, repair, verify, or upgrade the project's LLM Wiki from its config. Alias for lisa-wiki setup in the Lisa setup command family."
3
+ argument-hint: "[--upgrade] [--with-ci]"
4
+ ---
5
+
6
+ Use the lisa-wiki-setup skill to bring the wiki into conformance from wiki/lisa-wiki.config.json: validate config, ask purpose + README mode, scaffold the canonical structure, render the contract snapshot (stamping kernelVersion), seed the staff roster, then verify with /doctor. $ARGUMENTS