@codyswann/lisa 2.129.5 → 2.129.7

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 (111) hide show
  1. package/package.json +1 -1
  2. package/plugins/lisa/.claude-plugin/plugin.json +1 -1
  3. package/plugins/lisa/.codex-plugin/plugin.json +1 -1
  4. package/plugins/lisa/skills/atlassian-access/SKILL.md +32 -2
  5. package/plugins/lisa/skills/atlassian-access/scripts/markdown-to-adf.mjs +214 -0
  6. package/plugins/lisa/skills/confluence-to-tracker/SKILL.md +5 -3
  7. package/plugins/lisa/skills/github-build-intake/SKILL.md +14 -6
  8. package/plugins/lisa/skills/github-to-tracker/SKILL.md +5 -3
  9. package/plugins/lisa/skills/jira-build-intake/SKILL.md +16 -8
  10. package/plugins/lisa/skills/jira-read-ticket/SKILL.md +1 -1
  11. package/plugins/lisa/skills/jira-validate-ticket/SKILL.md +5 -5
  12. package/plugins/lisa/skills/jira-write-ticket/SKILL.md +9 -7
  13. package/plugins/lisa/skills/linear-build-intake/SKILL.md +16 -8
  14. package/plugins/lisa/skills/linear-to-tracker/SKILL.md +5 -3
  15. package/plugins/lisa/skills/notion-to-tracker/SKILL.md +5 -3
  16. package/plugins/lisa/skills/repair-intake/SKILL.md +147 -31
  17. package/plugins/lisa-agy/plugin.json +1 -1
  18. package/plugins/lisa-agy/skills/atlassian-access/SKILL.md +32 -2
  19. package/plugins/lisa-agy/skills/atlassian-access/scripts/markdown-to-adf.mjs +214 -0
  20. package/plugins/lisa-agy/skills/confluence-to-tracker/SKILL.md +5 -3
  21. package/plugins/lisa-agy/skills/github-build-intake/SKILL.md +14 -6
  22. package/plugins/lisa-agy/skills/github-to-tracker/SKILL.md +5 -3
  23. package/plugins/lisa-agy/skills/jira-build-intake/SKILL.md +16 -8
  24. package/plugins/lisa-agy/skills/jira-read-ticket/SKILL.md +1 -1
  25. package/plugins/lisa-agy/skills/jira-validate-ticket/SKILL.md +5 -5
  26. package/plugins/lisa-agy/skills/jira-write-ticket/SKILL.md +9 -7
  27. package/plugins/lisa-agy/skills/linear-build-intake/SKILL.md +16 -8
  28. package/plugins/lisa-agy/skills/linear-to-tracker/SKILL.md +5 -3
  29. package/plugins/lisa-agy/skills/notion-to-tracker/SKILL.md +5 -3
  30. package/plugins/lisa-agy/skills/repair-intake/SKILL.md +147 -31
  31. package/plugins/lisa-cdk/.claude-plugin/plugin.json +1 -1
  32. package/plugins/lisa-cdk/.codex-plugin/plugin.json +1 -1
  33. package/plugins/lisa-cdk-agy/plugin.json +1 -1
  34. package/plugins/lisa-cdk-copilot/.claude-plugin/plugin.json +1 -1
  35. package/plugins/lisa-cdk-cursor/.claude-plugin/plugin.json +1 -1
  36. package/plugins/lisa-copilot/.claude-plugin/plugin.json +1 -1
  37. package/plugins/lisa-copilot/skills/atlassian-access/SKILL.md +32 -2
  38. package/plugins/lisa-copilot/skills/atlassian-access/scripts/markdown-to-adf.mjs +214 -0
  39. package/plugins/lisa-copilot/skills/confluence-to-tracker/SKILL.md +5 -3
  40. package/plugins/lisa-copilot/skills/github-build-intake/SKILL.md +14 -6
  41. package/plugins/lisa-copilot/skills/github-to-tracker/SKILL.md +5 -3
  42. package/plugins/lisa-copilot/skills/jira-build-intake/SKILL.md +16 -8
  43. package/plugins/lisa-copilot/skills/jira-read-ticket/SKILL.md +1 -1
  44. package/plugins/lisa-copilot/skills/jira-validate-ticket/SKILL.md +5 -5
  45. package/plugins/lisa-copilot/skills/jira-write-ticket/SKILL.md +9 -7
  46. package/plugins/lisa-copilot/skills/linear-build-intake/SKILL.md +16 -8
  47. package/plugins/lisa-copilot/skills/linear-to-tracker/SKILL.md +5 -3
  48. package/plugins/lisa-copilot/skills/notion-to-tracker/SKILL.md +5 -3
  49. package/plugins/lisa-copilot/skills/repair-intake/SKILL.md +147 -31
  50. package/plugins/lisa-cursor/.claude-plugin/plugin.json +1 -1
  51. package/plugins/lisa-cursor/skills/atlassian-access/SKILL.md +32 -2
  52. package/plugins/lisa-cursor/skills/atlassian-access/scripts/markdown-to-adf.mjs +214 -0
  53. package/plugins/lisa-cursor/skills/confluence-to-tracker/SKILL.md +5 -3
  54. package/plugins/lisa-cursor/skills/github-build-intake/SKILL.md +14 -6
  55. package/plugins/lisa-cursor/skills/github-to-tracker/SKILL.md +5 -3
  56. package/plugins/lisa-cursor/skills/jira-build-intake/SKILL.md +16 -8
  57. package/plugins/lisa-cursor/skills/jira-read-ticket/SKILL.md +1 -1
  58. package/plugins/lisa-cursor/skills/jira-validate-ticket/SKILL.md +5 -5
  59. package/plugins/lisa-cursor/skills/jira-write-ticket/SKILL.md +9 -7
  60. package/plugins/lisa-cursor/skills/linear-build-intake/SKILL.md +16 -8
  61. package/plugins/lisa-cursor/skills/linear-to-tracker/SKILL.md +5 -3
  62. package/plugins/lisa-cursor/skills/notion-to-tracker/SKILL.md +5 -3
  63. package/plugins/lisa-cursor/skills/repair-intake/SKILL.md +147 -31
  64. package/plugins/lisa-expo/.claude-plugin/plugin.json +1 -1
  65. package/plugins/lisa-expo/.codex-plugin/plugin.json +1 -1
  66. package/plugins/lisa-expo-agy/plugin.json +1 -1
  67. package/plugins/lisa-expo-copilot/.claude-plugin/plugin.json +1 -1
  68. package/plugins/lisa-expo-cursor/.claude-plugin/plugin.json +1 -1
  69. package/plugins/lisa-harper-fabric/.claude-plugin/plugin.json +1 -1
  70. package/plugins/lisa-harper-fabric/.codex-plugin/plugin.json +1 -1
  71. package/plugins/lisa-harper-fabric-agy/plugin.json +1 -1
  72. package/plugins/lisa-harper-fabric-copilot/.claude-plugin/plugin.json +1 -1
  73. package/plugins/lisa-harper-fabric-cursor/.claude-plugin/plugin.json +1 -1
  74. package/plugins/lisa-nestjs/.claude-plugin/plugin.json +1 -1
  75. package/plugins/lisa-nestjs/.codex-plugin/plugin.json +1 -1
  76. package/plugins/lisa-nestjs-agy/plugin.json +1 -1
  77. package/plugins/lisa-nestjs-copilot/.claude-plugin/plugin.json +1 -1
  78. package/plugins/lisa-nestjs-cursor/.claude-plugin/plugin.json +1 -1
  79. package/plugins/lisa-openclaw/.claude-plugin/plugin.json +1 -1
  80. package/plugins/lisa-openclaw/.codex-plugin/plugin.json +1 -1
  81. package/plugins/lisa-openclaw-agy/plugin.json +1 -1
  82. package/plugins/lisa-openclaw-copilot/.claude-plugin/plugin.json +1 -1
  83. package/plugins/lisa-openclaw-cursor/.claude-plugin/plugin.json +1 -1
  84. package/plugins/lisa-rails/.claude-plugin/plugin.json +1 -1
  85. package/plugins/lisa-rails/.codex-plugin/plugin.json +1 -1
  86. package/plugins/lisa-rails-agy/plugin.json +1 -1
  87. package/plugins/lisa-rails-copilot/.claude-plugin/plugin.json +1 -1
  88. package/plugins/lisa-rails-cursor/.claude-plugin/plugin.json +1 -1
  89. package/plugins/lisa-typescript/.claude-plugin/plugin.json +1 -1
  90. package/plugins/lisa-typescript/.codex-plugin/plugin.json +1 -1
  91. package/plugins/lisa-typescript-agy/plugin.json +1 -1
  92. package/plugins/lisa-typescript-copilot/.claude-plugin/plugin.json +1 -1
  93. package/plugins/lisa-typescript-cursor/.claude-plugin/plugin.json +1 -1
  94. package/plugins/lisa-wiki/.claude-plugin/plugin.json +1 -1
  95. package/plugins/lisa-wiki/.codex-plugin/plugin.json +1 -1
  96. package/plugins/lisa-wiki-agy/plugin.json +1 -1
  97. package/plugins/lisa-wiki-copilot/.claude-plugin/plugin.json +1 -1
  98. package/plugins/lisa-wiki-cursor/.claude-plugin/plugin.json +1 -1
  99. package/plugins/src/base/skills/atlassian-access/SKILL.md +32 -2
  100. package/plugins/src/base/skills/atlassian-access/scripts/markdown-to-adf.mjs +214 -0
  101. package/plugins/src/base/skills/confluence-to-tracker/SKILL.md +5 -3
  102. package/plugins/src/base/skills/github-build-intake/SKILL.md +14 -6
  103. package/plugins/src/base/skills/github-to-tracker/SKILL.md +5 -3
  104. package/plugins/src/base/skills/jira-build-intake/SKILL.md +16 -8
  105. package/plugins/src/base/skills/jira-read-ticket/SKILL.md +1 -1
  106. package/plugins/src/base/skills/jira-validate-ticket/SKILL.md +5 -5
  107. package/plugins/src/base/skills/jira-write-ticket/SKILL.md +9 -7
  108. package/plugins/src/base/skills/linear-build-intake/SKILL.md +16 -8
  109. package/plugins/src/base/skills/linear-to-tracker/SKILL.md +5 -3
  110. package/plugins/src/base/skills/notion-to-tracker/SKILL.md +5 -3
  111. package/plugins/src/base/skills/repair-intake/SKILL.md +147 -31
@@ -0,0 +1,214 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Convert the Markdown subset Lisa writes for JIRA descriptions into ADF.
4
+ *
5
+ * This intentionally supports the description shapes produced by Lisa skills:
6
+ * headings, paragraphs, fenced code blocks, bullet lists, numbered lists,
7
+ * inline code, and bold text. Unsupported Markdown stays as paragraph text
8
+ * instead of being dropped.
9
+ */
10
+
11
+ const ADF_VERSION = 1;
12
+
13
+ /**
14
+ * Convert a Markdown string into an Atlassian Document Format document.
15
+ * @param {string} markdown Markdown or wiki-heading text.
16
+ * @returns {{version: 1, type: "doc", content: Array<Record<string, unknown>>}}
17
+ */
18
+ export function markdownToAdf(markdown) {
19
+ const lines = String(markdown ?? "")
20
+ .replace(/\r\n?/g, "\n")
21
+ .split("\n");
22
+ const content = [];
23
+ let paragraph = [];
24
+ let list = null;
25
+ let fence = null;
26
+
27
+ const flushParagraph = () => {
28
+ if (paragraph.length === 0) {
29
+ return;
30
+ }
31
+ content.push(paragraphNode(paragraph.join(" ")));
32
+ paragraph = [];
33
+ };
34
+
35
+ const flushList = () => {
36
+ if (!list) {
37
+ return;
38
+ }
39
+ content.push({
40
+ type: list.ordered ? "orderedList" : "bulletList",
41
+ content: list.items.map(item => ({
42
+ type: "listItem",
43
+ content: [paragraphNode(item)],
44
+ })),
45
+ });
46
+ list = null;
47
+ };
48
+
49
+ const flushFence = () => {
50
+ if (!fence) {
51
+ return;
52
+ }
53
+ const attrs = fence.language ? { language: fence.language } : {};
54
+ content.push({
55
+ type: "codeBlock",
56
+ attrs,
57
+ content: [{ type: "text", text: fence.lines.join("\n") }],
58
+ });
59
+ fence = null;
60
+ };
61
+
62
+ for (const line of lines) {
63
+ const fenceMatch = line.match(/^```([A-Za-z0-9_-]+)?\s*$/);
64
+ if (fenceMatch) {
65
+ if (fence) {
66
+ flushFence();
67
+ } else {
68
+ flushParagraph();
69
+ flushList();
70
+ fence = { language: fenceMatch[1] ?? "", lines: [] };
71
+ }
72
+ continue;
73
+ }
74
+
75
+ if (fence) {
76
+ fence.lines.push(line);
77
+ continue;
78
+ }
79
+
80
+ const heading = parseHeading(line);
81
+ if (heading) {
82
+ flushParagraph();
83
+ flushList();
84
+ content.push({
85
+ type: "heading",
86
+ attrs: { level: heading.level },
87
+ content: parseInline(heading.text),
88
+ });
89
+ continue;
90
+ }
91
+
92
+ const unordered = line.match(/^\s*[-*]\s+(.+)$/);
93
+ if (unordered) {
94
+ flushParagraph();
95
+ if (!list || list.ordered) {
96
+ flushList();
97
+ list = { ordered: false, items: [] };
98
+ }
99
+ list.items.push(unordered[1]);
100
+ continue;
101
+ }
102
+
103
+ const ordered = line.match(/^\s*\d+[.)]\s+(.+)$/);
104
+ if (ordered) {
105
+ flushParagraph();
106
+ if (!list || !list.ordered) {
107
+ flushList();
108
+ list = { ordered: true, items: [] };
109
+ }
110
+ list.items.push(ordered[1]);
111
+ continue;
112
+ }
113
+
114
+ if (line.trim() === "") {
115
+ flushParagraph();
116
+ flushList();
117
+ continue;
118
+ }
119
+
120
+ flushList();
121
+ paragraph.push(line.trim());
122
+ }
123
+
124
+ flushFence();
125
+ flushParagraph();
126
+ flushList();
127
+
128
+ return {
129
+ version: ADF_VERSION,
130
+ type: "doc",
131
+ content,
132
+ };
133
+ }
134
+
135
+ /**
136
+ * Parse Markdown and common JIRA wiki-style headings.
137
+ * @param {string} line A single input line.
138
+ * @returns {{level: number, text: string} | null}
139
+ */
140
+ function parseHeading(line) {
141
+ const markdown = line.match(/^(#{1,6})\s+(.+)$/);
142
+ if (markdown) {
143
+ return { level: markdown[1].length, text: markdown[2].trim() };
144
+ }
145
+
146
+ const wiki = line.match(/^h([1-6])\.\s+(.+)$/i);
147
+ if (wiki) {
148
+ return { level: Number(wiki[1]), text: wiki[2].trim() };
149
+ }
150
+
151
+ return null;
152
+ }
153
+
154
+ /**
155
+ * Create an ADF paragraph node.
156
+ * @param {string} text Text content.
157
+ * @returns {{type: "paragraph", content: Array<Record<string, unknown>>}}
158
+ */
159
+ function paragraphNode(text) {
160
+ return {
161
+ type: "paragraph",
162
+ content: parseInline(text),
163
+ };
164
+ }
165
+
166
+ /**
167
+ * Parse Lisa's inline formatting subset.
168
+ * @param {string} text Text with optional `code` and **strong** spans.
169
+ * @returns {Array<Record<string, unknown>>}
170
+ */
171
+ function parseInline(text) {
172
+ const nodes = [];
173
+ const pattern = /(`([^`]+)`|\*\*([^*]+)\*\*)/g;
174
+ let lastIndex = 0;
175
+ let match;
176
+
177
+ while ((match = pattern.exec(text)) !== null) {
178
+ if (match.index > lastIndex) {
179
+ nodes.push({ type: "text", text: text.slice(lastIndex, match.index) });
180
+ }
181
+
182
+ if (match[2]) {
183
+ nodes.push({
184
+ type: "text",
185
+ text: match[2],
186
+ marks: [{ type: "code" }],
187
+ });
188
+ } else {
189
+ nodes.push({
190
+ type: "text",
191
+ text: match[3],
192
+ marks: [{ type: "strong" }],
193
+ });
194
+ }
195
+ lastIndex = pattern.lastIndex;
196
+ }
197
+
198
+ if (lastIndex < text.length) {
199
+ nodes.push({ type: "text", text: text.slice(lastIndex) });
200
+ }
201
+
202
+ return nodes.length > 0 ? nodes : [{ type: "text", text: "" }];
203
+ }
204
+
205
+ if (import.meta.url === `file://${process.argv[1]}`) {
206
+ let input = "";
207
+ process.stdin.setEncoding("utf8");
208
+ process.stdin.on("data", chunk => {
209
+ input += chunk;
210
+ });
211
+ process.stdin.on("end", () => {
212
+ process.stdout.write(`${JSON.stringify(markdownToAdf(input), null, 2)}\n`);
213
+ });
214
+ }
@@ -252,10 +252,12 @@ Capture each returned story key — Phase 5 needs it as the parent for sub-tasks
252
252
 
253
253
  **Auto-split cross-repo work before delegation.** For each candidate sub-task, apply `lisa:task-decomposition` step 1.5: if the work touches more than one repo, split it into one sub-task per repo under the same parent Story (e.g., `[backend-api] Add field` + `[mobile-app] Display field`), and encode the producer-before-consumer ordering via dependencies. Work units that may span repos (Epic, Story, Spike) stay as planned; work units that must be single-repo (Bug, Task, Sub-task, Improvement) are split now. Splitting is this skill's responsibility — the validator's S10 gate is `product_relevant: false` because cross-repo failures are decomposition errors caught here, not product questions sent back to the PRD.
254
254
 
255
+ **S10 hard gate repair loop.** Dry-run validation is not advisory. Before any Phase 5 write, every planned leaf spec MUST pass `lisa:tracker-validate --spec-only` for S10 Single-repo scope. If any Bug / Task / Sub-task / Improvement fails S10 (missing `Repository`, more than one repo, or cross-repo AC), stop the write path, auto-split or restamp the spec using `lisa:task-decomposition` step 1.5, add the repo bracket and `## Repository` / `h2. Repository` section, then re-run `lisa:tracker-validate --spec-only`. If S10 still fails after repair, abort the ticket write and record an internal Error in the dry-run report; do not create the ticket, do not bypass with direct vendor writes, and do not surface the `product_relevant: false` failure as a product clarification.
256
+
255
257
  Delegate sub-task creation to **parallel agents** (one per epic or batch of stories) for efficiency. **Every spawned agent must invoke `lisa:tracker-write` for each sub-task — no agent may invoke `lisa:atlassian-access` write operations directly.**
256
258
 
257
259
  Each sub-task MUST:
258
- 1. **Be scoped to exactly ONE repo** — indicated in brackets in the summary: `[repo-name]`
260
+ 1. **Be scoped to exactly ONE repo** — indicated in brackets in the summary: `[repo-name]` and in the description's `## Repository` / `h2. Repository` section
259
261
  2. **Include an Empirical Verification Plan** — real user-like verification, NOT unit tests, linting, or typechecking
260
262
 
261
263
  **Leaf-only build-ready (`leaf-only-lifecycle`)**: Sub-tasks are the **leaf work units** of the decomposition — they are the ONLY items in the hierarchy that receive the build-ready label. `lisa:tracker-write` applies `status:ready` here so downstream build intake (`lisa:tracker-build-intake`) claims the leaves and never the Epic or Stories. Apply `status:ready` to each Sub-task; never to its parent Story or Epic (Phases 3–4). `lisa:tracker-write` enforces the same invariant on the write side, so a Sub-task split into per-repo children (the cross-repo case above) carries build-ready on the children, not on any intermediate parent that gains child work.
@@ -328,7 +330,7 @@ For each sub-task, invoke `lisa:tracker-write` with:
328
330
  - parent: the parent story key
329
331
  - project_key: [PROJECT]
330
332
  - summary: prefixed with the repo in brackets, e.g. "[backend-api] Add audit log table"
331
- - description_body: a 3-section draft (Context / Technical Approach / Acceptance Criteria)
333
+ - description_body: a 3-section draft (Context / Technical Approach / Acceptance Criteria) plus `h2. Repository` naming exactly one repo
332
334
  - gherkin_acceptance_criteria: derived from the story's functional requirements
333
335
  - sign_in_account: [test user credentials from config — name + role + how to obtain]
334
336
  - target_environment: "dev"
@@ -337,7 +339,7 @@ For each sub-task, invoke `lisa:tracker-write` with:
337
339
  NOT unit tests, linting, or typechecking.
338
340
 
339
341
  Each sub-task must:
340
- 1. Be scoped to ONE repo only — repo named in brackets in the summary
342
+ 1. Be scoped to ONE repo only — repo named in brackets in the summary and in `h2. Repository`
341
343
  2. Include the Empirical Verification Plan in the description
342
344
  3. Be created via `lisa:tracker-write`, not via direct `lisa:atlassian-access` write operations
343
345
 
@@ -264,24 +264,30 @@ Invoke `lisa:github-agent` (the per-issue lifecycle agent) with the issue ref. `
264
264
 
265
265
  Wait for `lisa:github-agent` to return. Capture its outcome:
266
266
 
267
- - **Success** — PR is ready (open or merged); evidence posted; ready for next status.
267
+ - **Success** — the build flow completed and a PR exists; evidence posted. The PR may already be **merged** or still **open** (auto-merge enabled, awaiting checks/merge). "Success" means the build work is sound — it does **not** assert the change reached an environment. The env transition in 3d gates on the PR actually being merged; an open PR does not advance the issue to a `done` env status.
268
268
  - **Blocked by github-verify pre-flight gate** — `lisa:github-agent` itself relabels the issue to `status:blocked` (or removes `$CLAIMED` and reassigns to the original author). This is correct and expected — let it stand. Record and move on.
269
269
  - **Blocked by ticket-triage ambiguities** — `lisa:github-agent` posts findings and stops. The issue stays in `$CLAIMED`. Surface to human; do not auto-relabel. Record under "Errors".
270
270
  - **Errored** — exception, missing config, etc. Leave the issue in `$CLAIMED` for human investigation. Record under "Errors".
271
271
 
272
- #### 3d. Transition to $DONE (only on Success)
272
+ #### 3d. Transition to $DONE (only after the PR is merged)
273
+
274
+ A `done` env state (`status:on-dev`, `status:on-stg`, or the terminal value) asserts that the code has actually reached that environment. Never set it for a PR that is merely open: auto-merge can be blocked indefinitely (a required rebase / `BEHIND` branch, failing checks, an unaddressed review), and the change may never land. Relabeling an issue `status:on-stg` on an open PR makes it *claim* a deploy that never happened. Transition only after confirming the PR merged.
273
275
 
274
276
  If `lisa:github-agent` returned Success:
275
277
 
276
- 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.
277
- 2. Determine whether `$DONE` is the true terminal done value per the `leaf-only-lifecycle` rule's Terminal native closure section:
278
+ 1. **Confirm the PR merged.** Read the live state of the issue's PR `gh pr view <pr> --json state,mergedAt,mergeStateStatus,url`:
279
+ - **Merged** (`state == MERGED`) → proceed to resolve and apply `$DONE` below. Where the env deploy is observable (a deploy workflow run / deployment status keyed to the merged-into branch via `deploy.branches`), confirm it did not fail before relabeling; a still-running deploy is treated like an open PR (leave in `$CLAIMED`), a failed deploy is recorded as an Error.
280
+ - **Open / not yet merged** → do **not** transition. The build is sound but the change has reached no environment yet. Record the issue under **"PR open — awaiting merge"** in the summary (with the PR URL and its `mergeStateStatus`), leave it in `$CLAIMED`, and stop. A later `lisa:repair-intake` cycle drives the open PR to merge — re-syncing a `BEHIND` branch so the already-enabled auto-merge can land, or surfacing a real blocker — and, once merged, applies this same env transition. Do **not** comment "Build complete" or close anything.
281
+ - **Closed without merging** → record an Error (the PR was abandoned unmerged); leave the issue in `$CLAIMED`.
282
+ 2. 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.
283
+ 3. Determine whether `$DONE` is the true terminal done value per the `leaf-only-lifecycle` rule's Terminal native closure section:
278
284
  - If `github.labels.build.done` is a string, that string is terminal.
279
285
  - If `github.labels.build.done` is an object, only the production/final environment value is terminal (default: `status:done`). Intermediate env values such as `status:on-dev` and `status:on-stg` are not terminal and must stay open.
280
286
  - If the project uses a different final environment name, resolve it from the configured deployment topology; if ambiguous, record an Error and do not close.
281
287
 
282
288
  ```bash
283
289
  gh issue edit <number> --repo <org>/<repo> --remove-label "$CLAIMED" --add-label "$DONE"
284
- gh issue comment <number> --repo <org>/<repo> --body "[claude-build-intake] Build complete. PR <URL>. Transitioned to $DONE."
290
+ gh issue comment <number> --repo <org>/<repo> --body "[claude-build-intake] Build complete. PR <URL> merged. Transitioned to $DONE."
285
291
  ```
286
292
 
287
293
  If `$DONE` is terminal, immediately close the native GitHub issue:
@@ -308,8 +314,10 @@ Cycle started: <ISO timestamp>
308
314
  Cycle completed: <ISO timestamp>
309
315
 
310
316
  Issues processed: <n>
311
- - $DONE (build complete, PR ready): <n>
317
+ - $DONE (build complete, PR merged): <n>
312
318
  - <org>/<repo>#<number> <title> → PR <URL>
319
+ - PR open — awaiting merge (left in $CLAIMED for repair-intake): <n>
320
+ - <org>/<repo>#<number> <title> → PR <URL> (mergeStateStatus: <state>)
313
321
  - Repaired (container — leaf-only-lifecycle): <n>
314
322
  - <org>/<repo>#<number> <title> — build-ready on a parent/container; moved $READY → $CLAIMED without invoking lisa:github-agent; lifecycle-repair comment posted
315
323
  - Skipped (active blockers): <n>
@@ -237,10 +237,12 @@ Capture each returned story ref — Phase 5 needs it.
237
237
 
238
238
  **Auto-split cross-repo work before delegation.** For each candidate sub-task, apply `lisa:task-decomposition` step 1.5: if the work touches more than one repo, split it into one sub-task per repo under the same parent Story (e.g., `[backend-api] Add field` + `[mobile-app] Display field`), and encode the producer-before-consumer ordering via dependencies. The source GitHub PRD and its coordination containers (Epic, Story, Spike) may remain cross-repo. Build-ready work units may not: every Bug, Task, Sub-task, or Improvement written from this phase must resolve to exactly one repository. Splitting is this skill's responsibility — the validator's S10 gate is `product_relevant: false` because cross-repo failures are decomposition errors caught here, not product questions sent back to the PRD.
239
239
 
240
+ **S10 hard gate repair loop.** Dry-run validation is not advisory. Before any Phase 5 write, every planned leaf spec MUST pass `lisa:tracker-validate --spec-only` for S10 Single-repo scope. If any Bug / Task / Sub-task / Improvement fails S10 (missing `Repository`, more than one repo, or cross-repo AC), stop the write path, auto-split or restamp the spec using `lisa:task-decomposition` step 1.5, add the repo bracket and `## Repository` / `h2. Repository` section, then re-run `lisa:tracker-validate --spec-only`. If S10 still fails after repair, abort the ticket write and record an internal Error in the dry-run report; do not create the ticket, do not bypass with direct vendor writes, and do not surface the `product_relevant: false` failure as a product clarification.
241
+
240
242
  Delegate sub-task creation to **parallel agents** (one per epic or batch of stories) for efficiency. **Every spawned agent must invoke `lisa:tracker-write` for each sub-task — no agent may call `lisa:jira-write-ticket` / `lisa:github-write-issue` / `gh issue create` directly.**
241
243
 
242
244
  Each sub-task MUST:
243
- 1. **Be scoped to exactly ONE repo** — indicated in brackets in the summary: `[repo-name]`.
245
+ 1. **Be scoped to exactly ONE repo** — indicated in brackets in the summary: `[repo-name]` and in the body/description's `## Repository` / `h2. Repository` section.
244
246
  2. **Include an Empirical Verification Plan** — real user-like verification, NOT unit tests, linting, or typechecking.
245
247
 
246
248
  **Leaf-only build-ready (`leaf-only-lifecycle`)**: Sub-tasks are the **leaf work units** of the decomposition — they are the ONLY items in the hierarchy that receive the build-ready label. `lisa:tracker-write` applies `status:ready` here so downstream build intake (`lisa:github-build-intake`) claims the leaves and never the Epic or Stories. Apply `status:ready` to each Sub-task; never to its parent Story or Epic (Phases 3–4). `lisa:github-write-issue` enforces the same invariant on the write side, so a Sub-task split into per-repo children (the cross-repo case above) carries build-ready on the children, not on any intermediate parent that gains child work.
@@ -312,7 +314,7 @@ For each sub-task, invoke `lisa:tracker-write` with:
312
314
  - issue_type: "Sub-task"
313
315
  - parent_ref: the parent story ref
314
316
  - summary: prefixed with the repo in brackets, e.g. "[backend-api] Add audit log table"
315
- - body: a multi-section draft (Context / Technical Approach / Acceptance Criteria / etc.)
317
+ - body: a multi-section draft (Context / Technical Approach / Acceptance Criteria / etc.) plus `## Repository` / `h2. Repository` naming exactly one repo
316
318
  - gherkin_acceptance_criteria: derived from the story's functional requirements
317
319
  - sign_in_account: [test user credentials from config]
318
320
  - target_environment: "dev"
@@ -320,7 +322,7 @@ For each sub-task, invoke `lisa:tracker-write` with:
320
322
  browser flow, CLI check after deploy) using the test credentials. NOT unit tests.
321
323
 
322
324
  Each sub-task must:
323
- 1. Be scoped to ONE repo only — repo named in brackets in the summary.
325
+ 1. Be scoped to ONE repo only — repo named in brackets in the summary and in `## Repository` / `h2. Repository`.
324
326
  2. Include the Empirical Verification Plan in the body.
325
327
  3. Be created via `lisa:tracker-write`, not via direct calls.
326
328
 
@@ -193,22 +193,28 @@ Invoke the `lisa:jira-agent` (existing per-ticket lifecycle agent) with the tick
193
193
  - Posting evidence via `lisa:jira-evidence`
194
194
 
195
195
  Wait for `lisa:jira-agent` to return. Capture its outcome:
196
- - **Success** — PR is ready (open or merged); evidence posted; ready for next status.
196
+ - **Success** — the build flow completed and a PR exists; evidence posted. The PR may already be **merged** or still **open** (auto-merge enabled, awaiting checks/merge). "Success" means the build work is sound — it does **not** assert the change reached an environment. The env transition in 3d gates on the PR actually being merged; an open PR does not advance the ticket to a `done` env status.
197
197
  - **Blocked by jira-verify pre-flight gate** — `lisa:jira-agent` itself transitions the ticket to `Blocked` and reassigns to Reporter. This is correct and expected — let it stand. Record the outcome and move on.
198
198
  - **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>"`.
199
199
  - **Errored** — exception, missing config, etc. Leave the ticket in `$CLAIMED` for human investigation. Record under "Errors" with the exception summary.
200
200
 
201
- #### 3d. Transition to $DONE (only on Success)
201
+ #### 3d. Transition to $DONE (only after the PR is merged)
202
+
203
+ A `done` env status (`On Dev`, `On Stg`, or the terminal value) asserts that the code has actually reached that environment. Never set it for a PR that is merely open: auto-merge can be blocked indefinitely (a required rebase / `BEHIND` branch, failing checks, an unaddressed review), and the change may never land. Setting `On Stg` on an open PR makes a ticket *claim* a deploy that never happened. Transition only after confirming the PR merged.
202
204
 
203
205
  If `lisa:jira-agent` returned Success:
204
- 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.
205
- 2. Determine whether `$DONE` is the true terminal done value per the `leaf-only-lifecycle` rule's Terminal native closure section:
206
+ 1. **Confirm the PR merged.** Read the live state of the ticket's PR `gh pr view <pr> --json state,mergedAt,mergeStateStatus,url`:
207
+ - **Merged** (`state == MERGED`) → proceed to resolve and apply `$DONE` below. Where the env deploy is observable (a deploy workflow run / deployment status keyed to the merged-into branch via `deploy.branches`), confirm it did not fail before transitioning; a still-running deploy is treated like an open PR (leave in `$CLAIMED` for a later cycle), a failed deploy is recorded as an Error.
208
+ - **Open / not yet merged** → do **not** transition. The build is sound but the change has reached no environment yet. Record the ticket under **"PR open — awaiting merge"** in the summary (with the PR URL and its `mergeStateStatus`), leave it in `$CLAIMED`, and stop. A later `lisa:repair-intake` cycle drives the open PR to merge — re-syncing a `BEHIND` branch so the already-enabled auto-merge can land, or surfacing a real blocker — and, once it is merged, applies this same env transition. Do **not** comment "Build complete" or file anything: the work is in-flight, not done.
209
+ - **Closed without merging** → record an Error (the PR was abandoned unmerged); leave the ticket in `$CLAIMED` for human investigation.
210
+ 2. 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.
211
+ 3. Determine whether `$DONE` is the true terminal done value per the `leaf-only-lifecycle` rule's Terminal native closure section:
206
212
  - If `jira.workflow.done` is a string, that status is terminal.
207
213
  - If `jira.workflow.done` is an object, only the production/final environment value is terminal (default: `Done`). Intermediate env statuses such as `On Dev` and `On Stg` are not terminal and must remain unresolved / open.
208
214
  - If the project uses a different final environment name, resolve it from the configured deployment topology; if ambiguous, record an Error and do not finalize native resolution.
209
- 3. Invoke `lisa:atlassian-access` `operation: transition key: <TICKET> to: "$DONE"`.
210
- 4. If `$DONE` is terminal, verify the resulting JIRA issue is natively closed/resolved: status category is `Done`, and resolution is set when the project's workflow requires one. If the transition screen requires an explicit resolution, use the configured default resolution if present; otherwise record an Error naming the missing workflow setup rather than silently landing in an unresolved Done-named status.
211
- 5. Post a `[claude-build-intake]` comment via `lisa:atlassian-access` `operation: comment key: <TICKET> body: "Build complete. PR <URL>. Transitioned to $DONE."` Include whether terminal native resolution was verified, already satisfied, skipped for an intermediate env, or blocked by workflow setup.
215
+ 4. Invoke `lisa:atlassian-access` `operation: transition key: <TICKET> to: "$DONE"`.
216
+ 5. If `$DONE` is terminal, verify the resulting JIRA issue is natively closed/resolved: status category is `Done`, and resolution is set when the project's workflow requires one. If the transition screen requires an explicit resolution, use the configured default resolution if present; otherwise record an Error naming the missing workflow setup rather than silently landing in an unresolved Done-named status.
217
+ 6. Post a `[claude-build-intake]` comment via `lisa:atlassian-access` `operation: comment key: <TICKET> body: "Build complete. PR <URL> merged. Transitioned to $DONE."` Include whether terminal native resolution was verified, already satisfied, skipped for an intermediate env, or blocked by workflow setup.
212
218
 
213
219
  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.
214
220
 
@@ -226,8 +232,10 @@ Cycle started: <ISO timestamp>
226
232
  Cycle completed: <ISO timestamp>
227
233
 
228
234
  Tickets processed: <n>
229
- - $DONE (build complete, PR ready): <n>
235
+ - $DONE (build complete, PR merged): <n>
230
236
  - <ticket-key> <summary> → PR <URL>
237
+ - PR open — awaiting merge (left in $CLAIMED for repair-intake): <n>
238
+ - <ticket-key> <summary> → PR <URL> (mergeStateStatus: <state>)
231
239
  - Skipped (container — leaf-only-lifecycle): <n>
232
240
  - <ticket-key> <summary> — build-ready on a parent with open child work; lifecycle-repair comment posted
233
241
  - Blocked (pre-flight verify failed): <n>
@@ -19,7 +19,7 @@ Repository name for scoped comments and logs: `basename $(git rev-parse --show-t
19
19
 
20
20
  ## Phase 2 — Fetch Primary Ticket
21
21
 
22
- Invoke `lisa:atlassian-access` via the Skill tool with `operation: read-ticket key: <TICKET-KEY>` for the target ticket. Extract and preserve:
22
+ Invoke `lisa:atlassian-access` via the Skill tool with `operation: read-ticket key: <TICKET-KEY>` for the target ticket. The access layer MUST request an explicit full JIRA field set for this operation (`acli jira workitem view --fields '*all'`, REST `fields=*all`, or an equivalent substrate-specific field list). Do not accept or re-use a default `acli workitem view` payload; its default six-field response omits parent, components, labels, priority, and custom fields, which makes leaf-only, relationship-search, build-ready, and required-custom-field gates read false-empty data. Extract and preserve:
23
23
 
24
24
  ### Metadata
25
25
 
@@ -119,7 +119,7 @@ Category values are drawn from this fixed set:
119
119
 
120
120
  #### S3 — Description has all three audiences
121
121
 
122
- Description text must include all of these sections (case-insensitive `h2.` headings):
122
+ Description text must include all of these sections. For proposed specs, detect case-insensitive Markdown/wiki headings (`##` or `h2.`). For live JIRA tickets, extract section headings from ADF `heading` nodes first and fail if the description is one literal Markdown/wiki paragraph instead of structured ADF heading nodes:
123
123
  - `Context / Business Value` — stakeholder-facing
124
124
  - `Technical Approach` — developer-facing
125
125
  - `Acceptance Criteria` — coding-assistant-facing
@@ -152,7 +152,7 @@ When `issue_type ∉ {Bug, Epic}`, `parent_key` must be set. (Validity of the ke
152
152
 
153
153
  #### S8 — Target Backend Environment
154
154
 
155
- When `runtime_behavior_change = true`, description must contain `h2. Target Backend Environment` with one of `dev`, `staging`, `prod`. Skipped for doc-only / config-only / type-only / Epic.
155
+ When `runtime_behavior_change = true`, description must contain a `Target Backend Environment` section (`h2.` / `##` in proposed text, or an ADF heading in live JIRA) with one of `dev`, `staging`, `prod`. Skipped for doc-only / config-only / type-only / Epic.
156
156
 
157
157
  #### S9 — Sign-in Required
158
158
 
@@ -162,7 +162,7 @@ If the spec doesn't set `authenticated_surface`, infer it: scan the description
162
162
 
163
163
  #### S10 — Repository section, single-repo scope
164
164
 
165
- When `issue_type ∈ {Bug, Task, Sub-task, Improvement}` — or a **build-ready childless Story/Spike** (a claimable leaf per `leaf-only-lifecycle`) — description must contain `h2. Repository` naming exactly one repo. Multiple repos OR cross-repo references in AC: FAIL with recommendation `"Split into per-repo work units under a shared parent Story (see lisa:task-decomposition step 1.5)"`.
165
+ When `issue_type ∈ {Bug, Task, Sub-task, Improvement}` — or a **build-ready childless Story/Spike** (a claimable leaf per `leaf-only-lifecycle`) — description must contain a `Repository` section (`h2.` / `##` in proposed text, or an ADF heading in live JIRA) naming exactly one repo. Multiple repos OR cross-repo references in AC: FAIL with recommendation `"Split into per-repo work units under a shared parent Story (see lisa:task-decomposition step 1.5)"`.
166
166
 
167
167
  An **Epic**, or a **Story/Spike that still holds child work** (or is not build-ready): skipped (may span repos — coordination containers, not claimable leaf work units).
168
168
 
@@ -170,7 +170,7 @@ This gate is `product_relevant: false` because cross-repo work units are not a p
170
170
 
171
171
  #### S11 — Validation Journey present
172
172
 
173
- When `runtime_behavior_change = true`, description must contain `h2. Validation Journey`. Skipped for doc-only / config-only / type-only / Epic.
173
+ When `runtime_behavior_change = true`, description must contain a `Validation Journey` section (`h2.` / `##` in proposed text, or an ADF heading in live JIRA). Skipped for doc-only / config-only / type-only / Epic.
174
174
 
175
175
  The caller controls the strictness by passing `journey_followup: "auto"` or `journey_followup: "none"` in the spec:
176
176
  - `auto` (default): if the section is absent, return `FAIL` with remediation `"Invoke lisa:jira-add-journey to append the section after create"`. Callers like `lisa:jira-write-ticket` know to chain `lisa:jira-add-journey` automatically, so this counts as a fixable failure they can resolve in-line — they re-run validation after appending.
@@ -248,7 +248,7 @@ Use the same project-issue-type-metadata lookup from F1 (via `lisa:atlassian-acc
248
248
 
249
249
  ## Execution
250
250
 
251
- 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.
251
+ 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. When the fetched description is ADF, walk the document tree and extract section headings from ADF `heading` nodes, then collect the text between heading nodes for section-specific gates. If the fetched description is a single paragraph containing literal Markdown/wiki heading markers, treat that as a formatting failure rather than accepting substring matches. Otherwise parse the YAML spec.
252
252
  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`).
253
253
  3. Run every Specification gate in order. Collect PASS / FAIL / N/A with a one-line reason.
254
254
  4. Unless the caller passed `--spec-only` (dry-run), run every Feasibility gate. Collect results.
@@ -37,7 +37,7 @@ Required fields (stop and ask if missing — do not invent values):
37
37
  | Validation Journey | Runtime-behavior changes | Delegate to `/jira-add-journey` |
38
38
  | Target backend environment | Runtime-behavior changes | `dev` / `staging` / `prod`; recorded in description (Phase 3). Skip only for doc/config/type-only tickets. |
39
39
  | Sign-in account / credentials | Tickets that touch authenticated surfaces | Name the account (or source — 1Password item, env var, seeded fixture) and role; recorded in description (Phase 3). Omit when sign-in is not required. |
40
- | Single-repo scope | Bug, Task, Sub-task | These types MUST cover one repo only. If the work crosses repos, split it before creating. Epic / Spike / Story may span repos. |
40
+ | Single-repo scope | Bug, Task, Sub-task, Improvement | These leaf work units MUST cover one repo only. If the work crosses repos, split it before creating. Epic / Spike / Story may span repos. |
41
41
 
42
42
  Optional but recommended: assignee, components, fix versions, labels, sprint, story points, reporter.
43
43
 
@@ -82,10 +82,10 @@ h2. Sign-in Required
82
82
  means "no sign-in needed for this ticket."]
83
83
 
84
84
  h2. Repository
85
- [Required for Bug / Task / Sub-task. Name the single repo this ticket covers.
86
- If the work spans repos, this ticket type is wrong — split into per-repo
87
- Tasks/Subtasks under a parent Story or Epic. Epic / Spike / Story may
88
- list multiple repos.]
85
+ [Required for Bug / Task / Sub-task / Improvement. Name the single repo this ticket covers.
86
+ If the work spans repos, this ticket type is wrong — split into per-repo
87
+ Tasks/Subtasks under a parent Story or Epic. Epic / Spike / Story may
88
+ list multiple repos.]
89
89
 
90
90
  h2. Validation Journey
91
91
  [Delegate to /jira-add-journey if the ticket changes runtime behavior.
@@ -222,6 +222,8 @@ If the validator reports `PASS`, continue to Phase 6.
222
222
 
223
223
  ## Phase 6 — Create or Update
224
224
 
225
+ Before calling `lisa:atlassian-access`, keep the description in Lisa's normal Markdown/wiki-heading authoring shape; the access layer owns conversion through `scripts/markdown-to-adf.mjs` and writes ADF to JIRA. This is required because acli stores raw Markdown/wiki text as one literal paragraph when no ADF object is provided. Post-write verification must confirm the live description contains ADF `heading` nodes for the required sections, not literal `##` / `h2.` text in a paragraph.
226
+
225
227
  ### CREATE
226
228
 
227
229
  1. Invoke `lisa:atlassian-access` via the Skill tool with `operation: write-ticket payload: {...}` containing all Phase 2/3/5 fields and the epic parent from Phase 4a (CREATE form — no existing key).
@@ -239,7 +241,7 @@ If the validator reports `PASS`, continue to Phase 6.
239
241
 
240
242
  ## Phase 7 — Verify
241
243
 
242
- Call the `lisa:jira-verify` skill on the resulting ticket. `lisa:jira-verify` fetches the live ticket and runs `lisa:jira-validate-ticket` against it — same gates as Phase 5.5, but applied to what JIRA actually stored (catches anything dropped or reformatted on write). If it reports failures, fix them before returning. Do not report success on a ticket that fails verify.
244
+ Call the `lisa:jira-verify` skill on the resulting ticket. `lisa:jira-verify` fetches the live ticket and runs `lisa:jira-validate-ticket` against it — same gates as Phase 5.5, but applied to what JIRA actually stored (catches anything dropped or reformatted on write, including Markdown/wiki descriptions that degraded into one literal text paragraph instead of ADF heading nodes). If it reports failures, fix them before returning. Do not report success on a ticket that fails verify.
243
245
 
244
246
  ## Phase 8 — Announce
245
247
 
@@ -255,7 +257,7 @@ Skip this step only on UPDATE when no material change was made.
255
257
 
256
258
  - Never create a non-bug ticket without an epic parent.
257
259
  - Never skip relationship discovery — both the git history search AND the JQL search must run, and their outcomes must be recorded on the ticket. "None found" is acceptable only when it's documented.
258
- - Never create a Bug, Task, or Sub-task that spans multiple repos. Split it before creating.
260
+ - Never create a Bug, Task, Sub-task, or Improvement that spans multiple repos. Split it before creating.
259
261
  - Never include a runtime-behavior ticket without a target backend environment, and never include an authenticated-surface ticket without sign-in credentials in the description.
260
262
  - Never invent custom field values. If the project requires a field you don't have, stop and ask.
261
263
  - Never overwrite a description without reading the current version first.
@@ -203,22 +203,28 @@ Invoke `lisa:linear-agent` (per-Issue lifecycle agent) with the Issue identifier
203
203
  - Posting evidence via `lisa:linear-evidence`
204
204
 
205
205
  Wait for the agent to return. Capture its outcome:
206
- - **Success** — PR is ready (open or merged); evidence posted; ready for next status.
206
+ - **Success** — the build flow completed and a PR exists; evidence posted. The PR may already be **merged** or still **open** (auto-merge enabled, awaiting checks/merge). "Success" means the build work is sound — it does **not** assert the change reached an environment. The env transition in 3d gates on the PR actually being merged; an open PR does not advance the Issue to a `done` env status.
207
207
  - **Blocked by linear-verify pre-flight gate** — `lisa:linear-agent` itself relabels to `status:blocked` and assigns to creator. Let it stand. Record and move on.
208
208
  - **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".
209
209
  - **Errored** — exception, missing config, etc. Leave at `$CLAIMED`. Record with exception summary.
210
210
 
211
- #### 3d. Relabel to $DONE (only on Success)
211
+ #### 3d. Relabel to $DONE (only after the PR is merged)
212
+
213
+ A `done` env state (`status:on-dev`, `status:on-stg`, or the terminal value) asserts that the code has actually reached that environment. Never set it for a PR that is merely open: auto-merge can be blocked indefinitely (a required rebase / `BEHIND` branch, failing checks, an unaddressed review), and the change may never land. Relabeling an Issue `status:on-stg` on an open PR makes it *claim* a deploy that never happened. Transition only after confirming the PR merged.
212
214
 
213
215
  If `lisa:linear-agent` returned Success:
214
- 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.
215
- 2. Determine whether `$DONE` is the true terminal done value per the `leaf-only-lifecycle` rule's Terminal native closure section:
216
+ 1. **Confirm the PR merged.** Read the live state of the Issue's PR `gh pr view <pr> --json state,mergedAt,mergeStateStatus,url`:
217
+ - **Merged** (`state == MERGED`) → proceed to resolve and apply `$DONE` below. Where the env deploy is observable (a deploy workflow run / deployment status keyed to the merged-into branch via `deploy.branches`), confirm it did not fail before relabeling; a still-running deploy is treated like an open PR (leave at `$CLAIMED`), a failed deploy is recorded as an Error.
218
+ - **Open / not yet merged** → do **not** transition. The build is sound but the change has reached no environment yet. Record the Issue under **"PR open — awaiting merge"** in the summary (with the PR URL and its `mergeStateStatus`), leave it at `$CLAIMED`, and stop. A later `lisa:repair-intake` cycle drives the open PR to merge — re-syncing a `BEHIND` branch so the already-enabled auto-merge can land, or surfacing a real blocker — and, once merged, applies this same env transition. Do **not** comment "Build complete" or change the native state.
219
+ - **Closed without merging** → record an Error (the PR was abandoned unmerged); leave the Issue at `$CLAIMED`.
220
+ 2. 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.
221
+ 3. Determine whether `$DONE` is the true terminal done value per the `leaf-only-lifecycle` rule's Terminal native closure section:
216
222
  - If `linear.labels.build.done` is a string, that string is terminal.
217
223
  - If `linear.labels.build.done` is an object, only the production/final environment value is terminal (default: `status:done`). Intermediate env values such as `status:on-dev` and `status:on-stg` are not terminal and must keep the native Issue open.
218
224
  - If the project uses a different final environment name, resolve it from the configured deployment topology; if ambiguous, record an Error and do not change the native state.
219
- 3. Update labels via `mcp__linear-server__save_issue`: remove `$CLAIMED` (or `$REVIEW` if `lisa:linear-evidence` already moved it forward), add `$DONE`.
220
- 4. If `$DONE` is terminal, move the native Linear Issue state to the configured Done / Completed state. Resolve that state from project configuration if present; otherwise inspect the team workflow for a terminal state with `state.type = "completed"` and a name such as `Done` or `Completed`. If no terminal state can be resolved, record an Error and leave the labels as the source of truth — do not invent a state name.
221
- 5. Post a `[claude-build-intake]` comment: `"Build complete. PR <URL>. Transitioned to $DONE."` Include whether native closure was applied, already satisfied, skipped for an intermediate env, or unavailable for setup reasons.
225
+ 4. Update labels via `mcp__linear-server__save_issue`: remove `$CLAIMED` (or `$REVIEW` if `lisa:linear-evidence` already moved it forward), add `$DONE`.
226
+ 5. If `$DONE` is terminal, move the native Linear Issue state to the configured Done / Completed state. Resolve that state from project configuration if present; otherwise inspect the team workflow for a terminal state with `state.type = "completed"` and a name such as `Done` or `Completed`. If no terminal state can be resolved, record an Error and leave the labels as the source of truth — do not invent a state name.
227
+ 6. Post a `[claude-build-intake]` comment: `"Build complete. PR <URL> merged. Transitioned to $DONE."` Include whether native closure was applied, already satisfied, skipped for an intermediate env, or unavailable for setup reasons.
222
228
 
223
229
  For any non-Success outcome, do NOT transition. The Issue sits where the agent left it — humans take it from there.
224
230
 
@@ -236,8 +242,10 @@ Cycle started: <ISO timestamp>
236
242
  Cycle completed: <ISO timestamp>
237
243
 
238
244
  Issues processed: <n>
239
- - $DONE (build complete, PR ready): <n>
245
+ - $DONE (build complete, PR merged): <n>
240
246
  - <ID> <title> → PR <URL>
247
+ - PR open — awaiting merge (left at $CLAIMED for repair-intake): <n>
248
+ - <ID> <title> → PR <URL> (mergeStateStatus: <state>)
241
249
  - Skipped (container — leaf-only-lifecycle): <n>
242
250
  - <ID> <title> — build-ready on a parent with open child work; lifecycle-repair comment posted
243
251
  - status:blocked (pre-flight verify failed): <n>
@@ -233,10 +233,12 @@ Capture each returned story key — Phase 5 needs it as the parent for sub-tasks
233
233
 
234
234
  **Auto-split cross-repo work before delegation.** For each candidate sub-task, apply `lisa:task-decomposition` step 1.5: if the work touches more than one repo, split it into one sub-task per repo under the same parent Story (e.g., `[backend-api] Add field` + `[mobile-app] Display field`), and encode the producer-before-consumer ordering via dependencies. Work units that may span repos (Epic, Story, Spike) stay as planned; work units that must be single-repo (Bug, Task, Sub-task, Improvement) are split now. Splitting is this skill's responsibility — the validator's S10 gate is `product_relevant: false` because cross-repo failures are decomposition errors caught here, not product questions sent back to the PRD.
235
235
 
236
+ **S10 hard gate repair loop.** Dry-run validation is not advisory. Before any Phase 5 write, every planned leaf spec MUST pass `lisa:tracker-validate --spec-only` for S10 Single-repo scope. If any Bug / Task / Sub-task / Improvement fails S10 (missing `Repository`, more than one repo, or cross-repo AC), stop the write path, auto-split or restamp the spec using `lisa:task-decomposition` step 1.5, add the repo bracket and `## Repository` / `h2. Repository` section, then re-run `lisa:tracker-validate --spec-only`. If S10 still fails after repair, abort the ticket write and record an internal Error in the dry-run report; do not create the ticket, do not bypass with direct vendor writes, and do not surface the `product_relevant: false` failure as a product clarification.
237
+
236
238
  Delegate sub-task creation to **parallel agents** (one per epic or batch of stories) for efficiency. **Every spawned agent must invoke `lisa:tracker-write` for each sub-task — no agent may call `createJiraIssue` directly.**
237
239
 
238
240
  Each sub-task MUST:
239
- 1. **Be scoped to exactly ONE repo** — indicated in brackets in the summary: `[repo-name]`
241
+ 1. **Be scoped to exactly ONE repo** — indicated in brackets in the summary: `[repo-name]` and in the description's `## Repository` / `h2. Repository` section
240
242
  2. **Include an Empirical Verification Plan** — real user-like verification, NOT unit tests, linting, or typechecking
241
243
 
242
244
  **Leaf-only build-ready (`leaf-only-lifecycle`)**: Sub-tasks are the **leaf work units** of the decomposition — they are the ONLY items in the hierarchy that receive the build-ready label. `lisa:tracker-write` applies `status:ready` here so downstream build intake (`lisa:tracker-build-intake`) claims the leaves and never the Epic or Stories. Apply `status:ready` to each Sub-task; never to its parent Story or Epic (Phases 3–4). `lisa:tracker-write` enforces the same invariant on the write side, so a Sub-task split into per-repo children (the cross-repo case above) carries build-ready on the children, not on any intermediate parent that gains child work.
@@ -308,7 +310,7 @@ For each sub-task, invoke `lisa:tracker-write` with:
308
310
  - parent: the parent story key
309
311
  - project_key: [PROJECT]
310
312
  - summary: prefixed with the repo in brackets, e.g. "[backend-api] Add audit log table"
311
- - description_body: a 3-section draft (Context / Technical Approach / Acceptance Criteria)
313
+ - description_body: a 3-section draft (Context / Technical Approach / Acceptance Criteria) plus `h2. Repository` naming exactly one repo
312
314
  - gherkin_acceptance_criteria: derived from the story's functional requirements
313
315
  - sign_in_account: [test user credentials from config — name + role + how to obtain]
314
316
  - target_environment: "dev"
@@ -317,7 +319,7 @@ For each sub-task, invoke `lisa:tracker-write` with:
317
319
  NOT unit tests, linting, or typechecking.
318
320
 
319
321
  Each sub-task must:
320
- 1. Be scoped to ONE repo only — repo named in brackets in the summary
322
+ 1. Be scoped to ONE repo only — repo named in brackets in the summary and in `h2. Repository`
321
323
  2. Include the Empirical Verification Plan in the description
322
324
  3. Be created via `lisa:tracker-write`, not via direct MCP calls
323
325
 
@@ -237,10 +237,12 @@ When classification is ambiguous, err on the side of inclusion — a developer c
237
237
 
238
238
  **Auto-split cross-repo work before delegation.** For each candidate sub-task, apply `lisa:task-decomposition` step 1.5: if the work touches more than one repo, split it into one sub-task per repo under the same parent Story (e.g., `[backend-api] Add field` + `[mobile-app] Display field`), and encode the producer-before-consumer ordering via dependencies. Work units that may span repos (Epic, Story, Spike) stay as planned; work units that must be single-repo (Bug, Task, Sub-task, Improvement) are split now. Splitting is this skill's responsibility — the validator's S10 gate is `product_relevant: false` because cross-repo failures are decomposition errors caught here, not product questions sent back to the PRD.
239
239
 
240
+ **S10 hard gate repair loop.** Dry-run validation is not advisory. Before any Phase 5 write, every planned leaf spec MUST pass `lisa:tracker-validate --spec-only` for S10 Single-repo scope. If any Bug / Task / Sub-task / Improvement fails S10 (missing `Repository`, more than one repo, or cross-repo AC), stop the write path, auto-split or restamp the spec using `lisa:task-decomposition` step 1.5, add the repo bracket and `## Repository` / `h2. Repository` section, then re-run `lisa:tracker-validate --spec-only`. If S10 still fails after repair, abort the ticket write and record an internal Error in the dry-run report; do not create the ticket, do not bypass with direct vendor writes, and do not surface the `product_relevant: false` failure as a product clarification.
241
+
240
242
  Delegate sub-task creation to **parallel agents** (one per epic or batch of stories) for efficiency. **Every spawned agent must invoke `lisa:tracker-write` for each sub-task — no agent may call `createJiraIssue` directly.** This is non-negotiable; see the Agent Prompt Template at the bottom of this skill for the exact instructions to pass.
241
243
 
242
244
  Each sub-task MUST:
243
- 1. **Be scoped to exactly ONE repo** — indicated in brackets in the summary: `[repo-name]`. `lisa:tracker-write` enforces single-repo scope on Sub-task; cross-repo sub-tasks will be rejected and must be split before delegation.
245
+ 1. **Be scoped to exactly ONE repo** — indicated in brackets in the summary: `[repo-name]` and in the description's `## Repository` / `h2. Repository` section. `lisa:tracker-write` enforces single-repo scope on Sub-task; cross-repo or unscoped sub-tasks will be rejected and must be split/restamped before delegation.
244
246
  2. **Include an Empirical Verification Plan** — real user-like verification, NOT unit tests, linting, or typechecking
245
247
 
246
248
  **Leaf-only build-ready (`leaf-only-lifecycle`)**: Sub-tasks are the **leaf work units** of the decomposition — they are the ONLY items in the hierarchy that receive the build-ready label. `lisa:tracker-write` applies `status:ready` here so downstream build intake (`lisa:tracker-build-intake`) claims the leaves and never the Epic or Stories. Apply `status:ready` to each Sub-task; never to its parent Story or Epic (Phases 3–4). `lisa:tracker-write` enforces the same invariant on the write side, so a Sub-task split into per-repo children (the cross-repo case above) carries build-ready on the children, not on any intermediate parent that gains child work.
@@ -328,7 +330,7 @@ For each sub-task, invoke `lisa:tracker-write` with:
328
330
  - parent: the parent story key
329
331
  - project_key: [PROJECT]
330
332
  - summary: prefixed with the repo in brackets, e.g. "[backend-api] Add audit log table"
331
- - description_body: a 3-section draft (Context / Technical Approach / Acceptance Criteria)
333
+ - description_body: a 3-section draft (Context / Technical Approach / Acceptance Criteria) plus `h2. Repository` naming exactly one repo
332
334
  - gherkin_acceptance_criteria: derived from the story's functional requirements
333
335
  - sign_in_account: [test user credentials from config — name + role + how to obtain]
334
336
  - target_environment: "dev"
@@ -337,7 +339,7 @@ For each sub-task, invoke `lisa:tracker-write` with:
337
339
  NOT unit tests, linting, or typechecking.
338
340
 
339
341
  Each sub-task must:
340
- 1. Be scoped to ONE repo only — repo named in brackets in the summary
342
+ 1. Be scoped to ONE repo only — repo named in brackets in the summary and in `h2. Repository`
341
343
  2. Include the Empirical Verification Plan in the description
342
344
  3. Be created via `lisa:tracker-write`, not via direct MCP calls
343
345