@codyswann/lisa 2.129.5 → 2.129.6
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-to-tracker/SKILL.md +5 -3
- 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-to-tracker/SKILL.md +5 -3
- package/plugins/lisa/skills/notion-to-tracker/SKILL.md +5 -3
- 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-to-tracker/SKILL.md +5 -3
- 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-to-tracker/SKILL.md +5 -3
- package/plugins/lisa-agy/skills/notion-to-tracker/SKILL.md +5 -3
- 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-to-tracker/SKILL.md +5 -3
- 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-to-tracker/SKILL.md +5 -3
- package/plugins/lisa-copilot/skills/notion-to-tracker/SKILL.md +5 -3
- 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-to-tracker/SKILL.md +5 -3
- 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-to-tracker/SKILL.md +5 -3
- package/plugins/lisa-cursor/skills/notion-to-tracker/SKILL.md +5 -3
- 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-to-tracker/SKILL.md +5 -3
- 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-to-tracker/SKILL.md +5 -3
- package/plugins/src/base/skills/notion-to-tracker/SKILL.md +5 -3
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "lisa-openclaw",
|
|
3
|
-
"version": "2.129.
|
|
3
|
+
"version": "2.129.6",
|
|
4
4
|
"description": "Connect staff roles to Telegram or Slack via OpenClaw — facilitator/specialist hub-and-spoke routing and repo-coding topics, for Claude Code and Codex",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Cody Swann"
|
|
@@ -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
|
|
|
@@ -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
|
|
|
@@ -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.
|
|
@@ -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
|
|