@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.
- package/package.json +1 -1
- package/plugins/lisa/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa/skills/atlassian-access/SKILL.md +32 -2
- package/plugins/lisa/skills/atlassian-access/scripts/markdown-to-adf.mjs +214 -0
- package/plugins/lisa/skills/confluence-to-tracker/SKILL.md +5 -3
- package/plugins/lisa/skills/github-build-intake/SKILL.md +14 -6
- package/plugins/lisa/skills/github-to-tracker/SKILL.md +5 -3
- package/plugins/lisa/skills/jira-build-intake/SKILL.md +16 -8
- package/plugins/lisa/skills/jira-read-ticket/SKILL.md +1 -1
- package/plugins/lisa/skills/jira-validate-ticket/SKILL.md +5 -5
- package/plugins/lisa/skills/jira-write-ticket/SKILL.md +9 -7
- package/plugins/lisa/skills/linear-build-intake/SKILL.md +16 -8
- package/plugins/lisa/skills/linear-to-tracker/SKILL.md +5 -3
- package/plugins/lisa/skills/notion-to-tracker/SKILL.md +5 -3
- package/plugins/lisa/skills/repair-intake/SKILL.md +147 -31
- package/plugins/lisa-agy/plugin.json +1 -1
- package/plugins/lisa-agy/skills/atlassian-access/SKILL.md +32 -2
- package/plugins/lisa-agy/skills/atlassian-access/scripts/markdown-to-adf.mjs +214 -0
- package/plugins/lisa-agy/skills/confluence-to-tracker/SKILL.md +5 -3
- package/plugins/lisa-agy/skills/github-build-intake/SKILL.md +14 -6
- package/plugins/lisa-agy/skills/github-to-tracker/SKILL.md +5 -3
- package/plugins/lisa-agy/skills/jira-build-intake/SKILL.md +16 -8
- package/plugins/lisa-agy/skills/jira-read-ticket/SKILL.md +1 -1
- package/plugins/lisa-agy/skills/jira-validate-ticket/SKILL.md +5 -5
- package/plugins/lisa-agy/skills/jira-write-ticket/SKILL.md +9 -7
- package/plugins/lisa-agy/skills/linear-build-intake/SKILL.md +16 -8
- package/plugins/lisa-agy/skills/linear-to-tracker/SKILL.md +5 -3
- package/plugins/lisa-agy/skills/notion-to-tracker/SKILL.md +5 -3
- package/plugins/lisa-agy/skills/repair-intake/SKILL.md +147 -31
- package/plugins/lisa-cdk/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-cdk/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-cdk-agy/plugin.json +1 -1
- package/plugins/lisa-cdk-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-cdk-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-copilot/skills/atlassian-access/SKILL.md +32 -2
- package/plugins/lisa-copilot/skills/atlassian-access/scripts/markdown-to-adf.mjs +214 -0
- package/plugins/lisa-copilot/skills/confluence-to-tracker/SKILL.md +5 -3
- package/plugins/lisa-copilot/skills/github-build-intake/SKILL.md +14 -6
- package/plugins/lisa-copilot/skills/github-to-tracker/SKILL.md +5 -3
- package/plugins/lisa-copilot/skills/jira-build-intake/SKILL.md +16 -8
- package/plugins/lisa-copilot/skills/jira-read-ticket/SKILL.md +1 -1
- package/plugins/lisa-copilot/skills/jira-validate-ticket/SKILL.md +5 -5
- package/plugins/lisa-copilot/skills/jira-write-ticket/SKILL.md +9 -7
- package/plugins/lisa-copilot/skills/linear-build-intake/SKILL.md +16 -8
- package/plugins/lisa-copilot/skills/linear-to-tracker/SKILL.md +5 -3
- package/plugins/lisa-copilot/skills/notion-to-tracker/SKILL.md +5 -3
- package/plugins/lisa-copilot/skills/repair-intake/SKILL.md +147 -31
- package/plugins/lisa-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-cursor/skills/atlassian-access/SKILL.md +32 -2
- package/plugins/lisa-cursor/skills/atlassian-access/scripts/markdown-to-adf.mjs +214 -0
- package/plugins/lisa-cursor/skills/confluence-to-tracker/SKILL.md +5 -3
- package/plugins/lisa-cursor/skills/github-build-intake/SKILL.md +14 -6
- package/plugins/lisa-cursor/skills/github-to-tracker/SKILL.md +5 -3
- package/plugins/lisa-cursor/skills/jira-build-intake/SKILL.md +16 -8
- package/plugins/lisa-cursor/skills/jira-read-ticket/SKILL.md +1 -1
- package/plugins/lisa-cursor/skills/jira-validate-ticket/SKILL.md +5 -5
- package/plugins/lisa-cursor/skills/jira-write-ticket/SKILL.md +9 -7
- package/plugins/lisa-cursor/skills/linear-build-intake/SKILL.md +16 -8
- package/plugins/lisa-cursor/skills/linear-to-tracker/SKILL.md +5 -3
- package/plugins/lisa-cursor/skills/notion-to-tracker/SKILL.md +5 -3
- package/plugins/lisa-cursor/skills/repair-intake/SKILL.md +147 -31
- package/plugins/lisa-expo/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-expo/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-expo-agy/plugin.json +1 -1
- package/plugins/lisa-expo-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-expo-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-harper-fabric/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-harper-fabric/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-harper-fabric-agy/plugin.json +1 -1
- package/plugins/lisa-harper-fabric-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-harper-fabric-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-nestjs/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-nestjs/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-nestjs-agy/plugin.json +1 -1
- package/plugins/lisa-nestjs-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-nestjs-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-openclaw/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-openclaw/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-openclaw-agy/plugin.json +1 -1
- package/plugins/lisa-openclaw-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-openclaw-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-rails/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-rails/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-rails-agy/plugin.json +1 -1
- package/plugins/lisa-rails-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-rails-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-typescript/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-typescript/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-typescript-agy/plugin.json +1 -1
- package/plugins/lisa-typescript-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-typescript-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-wiki/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-wiki/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-wiki-agy/plugin.json +1 -1
- package/plugins/lisa-wiki-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-wiki-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/src/base/skills/atlassian-access/SKILL.md +32 -2
- package/plugins/src/base/skills/atlassian-access/scripts/markdown-to-adf.mjs +214 -0
- package/plugins/src/base/skills/confluence-to-tracker/SKILL.md +5 -3
- package/plugins/src/base/skills/github-build-intake/SKILL.md +14 -6
- package/plugins/src/base/skills/github-to-tracker/SKILL.md +5 -3
- package/plugins/src/base/skills/jira-build-intake/SKILL.md +16 -8
- package/plugins/src/base/skills/jira-read-ticket/SKILL.md +1 -1
- package/plugins/src/base/skills/jira-validate-ticket/SKILL.md +5 -5
- package/plugins/src/base/skills/jira-write-ticket/SKILL.md +9 -7
- package/plugins/src/base/skills/linear-build-intake/SKILL.md +16 -8
- package/plugins/src/base/skills/linear-to-tracker/SKILL.md +5 -3
- package/plugins/src/base/skills/notion-to-tracker/SKILL.md +5 -3
- package/plugins/src/base/skills/repair-intake/SKILL.md +147 -31
|
@@ -217,6 +217,36 @@ fi
|
|
|
217
217
|
|
|
218
218
|
If validation fails, never silently proceed — abort and instruct the user to fix env.
|
|
219
219
|
|
|
220
|
+
### Step 2.5 — JIRA description normalization
|
|
221
|
+
|
|
222
|
+
For `write-ticket` create/edit operations, normalize any Lisa-authored JIRA description before dispatching to a substrate. JIRA Cloud stores descriptions as Atlassian Document Format (ADF). `acli` does not convert Markdown or JIRA wiki markup to ADF; if plain text is passed to `--description` / `--description-file`, JIRA stores one literal paragraph containing strings like `## Repository` or `h2. Repository`. That breaks `jira-validate-ticket` heading checks and renders poorly for humans.
|
|
223
|
+
|
|
224
|
+
Use the shared converter at `scripts/markdown-to-adf.mjs` from this skill for every write path that carries a string description:
|
|
225
|
+
|
|
226
|
+
```bash
|
|
227
|
+
normalize_jira_description_payload() {
|
|
228
|
+
local payload_file="$1"
|
|
229
|
+
local converter="$(dirname "$0")/scripts/markdown-to-adf.mjs"
|
|
230
|
+
|
|
231
|
+
jq -e '.fields.description | type == "string"' "$payload_file" >/dev/null 2>&1 || return 0
|
|
232
|
+
|
|
233
|
+
local markdown_file adf_file
|
|
234
|
+
markdown_file=$(mktemp)
|
|
235
|
+
adf_file=$(mktemp)
|
|
236
|
+
jq -r '.fields.description' "$payload_file" > "$markdown_file"
|
|
237
|
+
node "$converter" < "$markdown_file" > "$adf_file"
|
|
238
|
+
jq --slurpfile adf "$adf_file" '.fields.description = $adf[0]' "$payload_file" > "$payload_file.tmp"
|
|
239
|
+
mv "$payload_file.tmp" "$payload_file"
|
|
240
|
+
}
|
|
241
|
+
```
|
|
242
|
+
|
|
243
|
+
Rules:
|
|
244
|
+
|
|
245
|
+
- Convert Markdown headings (`#` / `##` / `###`) and JIRA wiki headings (`h1.` / `h2.` / `h3.`) to ADF `heading` nodes.
|
|
246
|
+
- Convert fenced code blocks, bullet lists, numbered lists, paragraphs, inline code, and bold text to their ADF equivalents.
|
|
247
|
+
- Run this for acli, curl, and MCP writes unless the caller already supplied an ADF object (`description.type == "doc"`). Do not double-convert existing ADF.
|
|
248
|
+
- For acli, pass the normalized JSON through `--from-json`; do not use `--description` or `--description-file` with raw Markdown/wiki text.
|
|
249
|
+
|
|
220
250
|
### Step 3 — Operation dispatch
|
|
221
251
|
|
|
222
252
|
Substrate column meanings:
|
|
@@ -232,7 +262,7 @@ Substrate column meanings:
|
|
|
232
262
|
| Operation | acli adapter | MCP adapter | curl adapter |
|
|
233
263
|
|---|---|---|---|
|
|
234
264
|
| **JIRA ops** | | | |
|
|
235
|
-
| `read-ticket key:<K>` | `acli jira workitem view <K> --json` | `mcp__plugin_atlassian_atlassian__getJiraIssue` | `GET https://<SITE>/rest/api/3/issue/<K
|
|
265
|
+
| `read-ticket key:<K>` | `acli jira workitem view <K> --fields '*all' --json` | `mcp__plugin_atlassian_atlassian__getJiraIssue` | `GET https://<SITE>/rest/api/3/issue/<K>?fields=*all` |
|
|
236
266
|
| `write-ticket payload:<P>` (create) | `acli jira workitem create --from-json <P>` | `mcp__plugin_atlassian_atlassian__createJiraIssue` | `POST https://<SITE>/rest/api/3/issue` body=`<P>` |
|
|
237
267
|
| `write-ticket payload:<P>` (edit) | `acli jira workitem edit <K> --from-json <P>` | `mcp__plugin_atlassian_atlassian__editJiraIssue` | `PUT https://<SITE>/rest/api/3/issue/<K>` body=`<P>` |
|
|
238
268
|
| `transition key:<K> to:<S>` | `acli jira workitem transition --key <K> --status "<S>" --yes` | `mcp__plugin_atlassian_atlassian__transitionJiraIssue` | resolve transition id then `POST .../issue/<K>/transitions` |
|
|
@@ -259,7 +289,7 @@ Substrate column meanings:
|
|
|
259
289
|
|
|
260
290
|
**Confluence v1 vs v2:** every Confluence curl path above uses **v1** (`/wiki/rest/api/...`). v1 is deprecated by Atlassian but as of writing remains functional for API-token Basic auth. The v2 API (`/api/v2/...`) requires *granular* OAuth scopes that aren't issued to Basic-auth API tokens consistently — so v1 is the safer path for now. When Atlassian fully retires v1, this table must move to v2 (the dispatch is the only thing that changes; the substrate-selection logic is unaffected).
|
|
261
291
|
|
|
262
|
-
**acli flag note:** acli's `--output` flag does not exist; the correct flag is `--json`. List commands require `--paginate` or `--limit` (no implicit fetch-all). Several documented adapters are nominal — verify against `acli <subcmd> --help` before relying on them. When acli's adapter is broken or missing for a specific op, fall through to MCP (if identity-matched) then curl per the tier ordering.
|
|
292
|
+
**acli flag note:** acli's `--output` flag does not exist; the correct flag is `--json`. List commands require `--paginate` or `--limit` (no implicit fetch-all). `acli jira workitem view` defaults to a restricted field set (`key,issuetype,summary,status,assignee,description`), so `read-ticket` MUST pass `--fields '*all'` or an explicit equivalent that includes every downstream dependency: parent, subtasks, issue links, components, labels, priority, status, issue type, summary, description, fix versions, affected versions, attachments, comments, estimates, sprint/story-point fields, and project-required custom fields. Never rely on the default view fields; they hide parent/components/labels and corrupt leaf-only, relationship-search, build-ready, and required-custom-field gates. Several documented adapters are nominal — verify against `acli <subcmd> --help` before relying on them. When acli's adapter is broken or missing for a specific op, fall through to MCP (if identity-matched) then curl per the tier ordering.
|
|
263
293
|
|
|
264
294
|
**JIRA terminal-resolution note:** when a caller marks a transition as terminal per `leaf-only-lifecycle`, the substrate must not treat a Done-named status as sufficient by name alone. After `transition key:<K> to:<S>`, re-read the issue and verify `statusCategory = Done`; if the workflow requires a resolution, verify `resolution` is set. If the transition screen requires a resolution value, pass the configured default resolution when available; otherwise return a setup error so the build-intake skill can report the workflow gap instead of silently leaving an unresolved ticket in a Done-looking status.
|
|
265
295
|
|
|
@@ -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
|
|
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
|
|
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.
|
|
277
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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.
|
|
205
|
-
|
|
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
|
-
|
|
210
|
-
|
|
211
|
-
|
|
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
|
|
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
|
|
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 `
|
|
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 `
|
|
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 `
|
|
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
|
|
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
|
-
|
|
87
|
-
|
|
88
|
-
|
|
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,
|
|
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
|
|
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
|
|
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.
|
|
215
|
-
|
|
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
|
-
|
|
220
|
-
|
|
221
|
-
|
|
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
|
|
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>
|