@codyswann/lisa 2.129.4 → 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.
Files changed (91) hide show
  1. package/package.json +1 -1
  2. package/plugins/lisa/.claude-plugin/plugin.json +1 -1
  3. package/plugins/lisa/.codex-plugin/plugin.json +1 -1
  4. package/plugins/lisa/skills/atlassian-access/SKILL.md +32 -2
  5. package/plugins/lisa/skills/atlassian-access/scripts/markdown-to-adf.mjs +214 -0
  6. package/plugins/lisa/skills/confluence-to-tracker/SKILL.md +5 -3
  7. package/plugins/lisa/skills/github-to-tracker/SKILL.md +5 -3
  8. package/plugins/lisa/skills/jira-read-ticket/SKILL.md +1 -1
  9. package/plugins/lisa/skills/jira-validate-ticket/SKILL.md +5 -5
  10. package/plugins/lisa/skills/jira-write-ticket/SKILL.md +9 -7
  11. package/plugins/lisa/skills/linear-to-tracker/SKILL.md +5 -3
  12. package/plugins/lisa/skills/notion-to-tracker/SKILL.md +5 -3
  13. package/plugins/lisa-agy/plugin.json +1 -1
  14. package/plugins/lisa-agy/skills/atlassian-access/SKILL.md +32 -2
  15. package/plugins/lisa-agy/skills/atlassian-access/scripts/markdown-to-adf.mjs +214 -0
  16. package/plugins/lisa-agy/skills/confluence-to-tracker/SKILL.md +5 -3
  17. package/plugins/lisa-agy/skills/github-to-tracker/SKILL.md +5 -3
  18. package/plugins/lisa-agy/skills/jira-read-ticket/SKILL.md +1 -1
  19. package/plugins/lisa-agy/skills/jira-validate-ticket/SKILL.md +5 -5
  20. package/plugins/lisa-agy/skills/jira-write-ticket/SKILL.md +9 -7
  21. package/plugins/lisa-agy/skills/linear-to-tracker/SKILL.md +5 -3
  22. package/plugins/lisa-agy/skills/notion-to-tracker/SKILL.md +5 -3
  23. package/plugins/lisa-cdk/.claude-plugin/plugin.json +1 -1
  24. package/plugins/lisa-cdk/.codex-plugin/plugin.json +1 -1
  25. package/plugins/lisa-cdk-agy/plugin.json +1 -1
  26. package/plugins/lisa-cdk-copilot/.claude-plugin/plugin.json +1 -1
  27. package/plugins/lisa-cdk-cursor/.claude-plugin/plugin.json +1 -1
  28. package/plugins/lisa-copilot/.claude-plugin/plugin.json +1 -1
  29. package/plugins/lisa-copilot/skills/atlassian-access/SKILL.md +32 -2
  30. package/plugins/lisa-copilot/skills/atlassian-access/scripts/markdown-to-adf.mjs +214 -0
  31. package/plugins/lisa-copilot/skills/confluence-to-tracker/SKILL.md +5 -3
  32. package/plugins/lisa-copilot/skills/github-to-tracker/SKILL.md +5 -3
  33. package/plugins/lisa-copilot/skills/jira-read-ticket/SKILL.md +1 -1
  34. package/plugins/lisa-copilot/skills/jira-validate-ticket/SKILL.md +5 -5
  35. package/plugins/lisa-copilot/skills/jira-write-ticket/SKILL.md +9 -7
  36. package/plugins/lisa-copilot/skills/linear-to-tracker/SKILL.md +5 -3
  37. package/plugins/lisa-copilot/skills/notion-to-tracker/SKILL.md +5 -3
  38. package/plugins/lisa-cursor/.claude-plugin/plugin.json +1 -1
  39. package/plugins/lisa-cursor/skills/atlassian-access/SKILL.md +32 -2
  40. package/plugins/lisa-cursor/skills/atlassian-access/scripts/markdown-to-adf.mjs +214 -0
  41. package/plugins/lisa-cursor/skills/confluence-to-tracker/SKILL.md +5 -3
  42. package/plugins/lisa-cursor/skills/github-to-tracker/SKILL.md +5 -3
  43. package/plugins/lisa-cursor/skills/jira-read-ticket/SKILL.md +1 -1
  44. package/plugins/lisa-cursor/skills/jira-validate-ticket/SKILL.md +5 -5
  45. package/plugins/lisa-cursor/skills/jira-write-ticket/SKILL.md +9 -7
  46. package/plugins/lisa-cursor/skills/linear-to-tracker/SKILL.md +5 -3
  47. package/plugins/lisa-cursor/skills/notion-to-tracker/SKILL.md +5 -3
  48. package/plugins/lisa-expo/.claude-plugin/plugin.json +1 -1
  49. package/plugins/lisa-expo/.codex-plugin/plugin.json +1 -1
  50. package/plugins/lisa-expo-agy/plugin.json +1 -1
  51. package/plugins/lisa-expo-copilot/.claude-plugin/plugin.json +1 -1
  52. package/plugins/lisa-expo-cursor/.claude-plugin/plugin.json +1 -1
  53. package/plugins/lisa-harper-fabric/.claude-plugin/plugin.json +1 -1
  54. package/plugins/lisa-harper-fabric/.codex-plugin/plugin.json +1 -1
  55. package/plugins/lisa-harper-fabric-agy/plugin.json +1 -1
  56. package/plugins/lisa-harper-fabric-copilot/.claude-plugin/plugin.json +1 -1
  57. package/plugins/lisa-harper-fabric-cursor/.claude-plugin/plugin.json +1 -1
  58. package/plugins/lisa-nestjs/.claude-plugin/plugin.json +1 -1
  59. package/plugins/lisa-nestjs/.codex-plugin/plugin.json +1 -1
  60. package/plugins/lisa-nestjs-agy/plugin.json +1 -1
  61. package/plugins/lisa-nestjs-copilot/.claude-plugin/plugin.json +1 -1
  62. package/plugins/lisa-nestjs-cursor/.claude-plugin/plugin.json +1 -1
  63. package/plugins/lisa-openclaw/.claude-plugin/plugin.json +1 -1
  64. package/plugins/lisa-openclaw/.codex-plugin/plugin.json +1 -1
  65. package/plugins/lisa-openclaw-agy/plugin.json +1 -1
  66. package/plugins/lisa-openclaw-copilot/.claude-plugin/plugin.json +1 -1
  67. package/plugins/lisa-openclaw-cursor/.claude-plugin/plugin.json +1 -1
  68. package/plugins/lisa-rails/.claude-plugin/plugin.json +1 -1
  69. package/plugins/lisa-rails/.codex-plugin/plugin.json +1 -1
  70. package/plugins/lisa-rails-agy/plugin.json +1 -1
  71. package/plugins/lisa-rails-copilot/.claude-plugin/plugin.json +1 -1
  72. package/plugins/lisa-rails-cursor/.claude-plugin/plugin.json +1 -1
  73. package/plugins/lisa-typescript/.claude-plugin/plugin.json +1 -1
  74. package/plugins/lisa-typescript/.codex-plugin/plugin.json +1 -1
  75. package/plugins/lisa-typescript-agy/plugin.json +1 -1
  76. package/plugins/lisa-typescript-copilot/.claude-plugin/plugin.json +1 -1
  77. package/plugins/lisa-typescript-cursor/.claude-plugin/plugin.json +1 -1
  78. package/plugins/lisa-wiki/.claude-plugin/plugin.json +1 -1
  79. package/plugins/lisa-wiki/.codex-plugin/plugin.json +1 -1
  80. package/plugins/lisa-wiki-agy/plugin.json +1 -1
  81. package/plugins/lisa-wiki-copilot/.claude-plugin/plugin.json +1 -1
  82. package/plugins/lisa-wiki-cursor/.claude-plugin/plugin.json +1 -1
  83. package/plugins/src/base/skills/atlassian-access/SKILL.md +32 -2
  84. package/plugins/src/base/skills/atlassian-access/scripts/markdown-to-adf.mjs +214 -0
  85. package/plugins/src/base/skills/confluence-to-tracker/SKILL.md +5 -3
  86. package/plugins/src/base/skills/github-to-tracker/SKILL.md +5 -3
  87. package/plugins/src/base/skills/jira-read-ticket/SKILL.md +1 -1
  88. package/plugins/src/base/skills/jira-validate-ticket/SKILL.md +5 -5
  89. package/plugins/src/base/skills/jira-write-ticket/SKILL.md +9 -7
  90. package/plugins/src/base/skills/linear-to-tracker/SKILL.md +5 -3
  91. 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.4",
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"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-rails",
3
- "version": "2.129.4",
3
+ "version": "2.129.6",
4
4
  "description": "Ruby on Rails-specific hooks — RuboCop linting/formatting and ast-grep scanning on edit",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-rails",
3
- "version": "2.129.4",
3
+ "version": "2.129.6",
4
4
  "description": "Ruby on Rails-specific skills and hooks for RuboCop and ast-grep scanning on edit.",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-rails",
3
- "version": "2.129.4",
3
+ "version": "2.129.6",
4
4
  "description": "Ruby on Rails-specific hooks — RuboCop linting/formatting and ast-grep scanning on edit",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-rails",
3
- "version": "2.129.4",
3
+ "version": "2.129.6",
4
4
  "description": "Ruby on Rails-specific hooks — RuboCop linting/formatting and ast-grep scanning on edit",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-rails",
3
- "version": "2.129.4",
3
+ "version": "2.129.6",
4
4
  "description": "Ruby on Rails-specific hooks — RuboCop linting/formatting and ast-grep scanning on edit",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-typescript",
3
- "version": "2.129.4",
3
+ "version": "2.129.6",
4
4
  "description": "TypeScript-specific hooks — Prettier formatting, ESLint linting, ast-grep scanning, and error-suppression blocking on edit",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-typescript",
3
- "version": "2.129.4",
3
+ "version": "2.129.6",
4
4
  "description": "TypeScript-specific hooks for formatting, linting, and ast-grep scanning on edit.",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-typescript",
3
- "version": "2.129.4",
3
+ "version": "2.129.6",
4
4
  "description": "TypeScript-specific hooks — Prettier formatting, ESLint linting, ast-grep scanning, and error-suppression blocking on edit",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-typescript",
3
- "version": "2.129.4",
3
+ "version": "2.129.6",
4
4
  "description": "TypeScript-specific hooks — Prettier formatting, ESLint linting, ast-grep scanning, and error-suppression blocking on edit",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-typescript",
3
- "version": "2.129.4",
3
+ "version": "2.129.6",
4
4
  "description": "TypeScript-specific hooks — Prettier formatting, ESLint linting, ast-grep scanning, and error-suppression blocking on edit",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-wiki",
3
- "version": "2.129.4",
3
+ "version": "2.129.6",
4
4
  "description": "LLM Wiki — a distributable, git-native markdown knowledge base for Claude Code and Codex",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-wiki",
3
- "version": "2.129.4",
3
+ "version": "2.129.6",
4
4
  "description": "Distributable LLM Wiki kernel — ingest, query, lint, and maintain a git-native markdown knowledge base across Claude and Codex.",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-wiki",
3
- "version": "2.129.4",
3
+ "version": "2.129.6",
4
4
  "description": "LLM Wiki — a distributable, git-native markdown knowledge base for Claude Code and Codex",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-wiki",
3
- "version": "2.129.4",
3
+ "version": "2.129.6",
4
4
  "description": "LLM Wiki — a distributable, git-native markdown knowledge base for Claude Code and Codex",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-wiki",
3
- "version": "2.129.4",
3
+ "version": "2.129.6",
4
4
  "description": "LLM Wiki — a distributable, git-native markdown knowledge base for Claude Code and Codex",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -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 (case-insensitive `h2.` headings):
122
+ Description text must include all of these sections. For proposed specs, detect case-insensitive Markdown/wiki headings (`##` or `h2.`). For live JIRA tickets, extract section headings from ADF `heading` nodes first and fail if the description is one literal Markdown/wiki paragraph instead of structured ADF heading nodes:
123
123
  - `Context / Business Value` — stakeholder-facing
124
124
  - `Technical Approach` — developer-facing
125
125
  - `Acceptance Criteria` — coding-assistant-facing
@@ -152,7 +152,7 @@ When `issue_type ∉ {Bug, Epic}`, `parent_key` must be set. (Validity of the ke
152
152
 
153
153
  #### S8 — Target Backend Environment
154
154
 
155
- When `runtime_behavior_change = true`, description must contain `h2. Target Backend Environment` with one of `dev`, `staging`, `prod`. Skipped for doc-only / config-only / type-only / Epic.
155
+ When `runtime_behavior_change = true`, description must contain a `Target Backend Environment` section (`h2.` / `##` in proposed text, or an ADF heading in live JIRA) with one of `dev`, `staging`, `prod`. Skipped for doc-only / config-only / type-only / Epic.
156
156
 
157
157
  #### S9 — Sign-in Required
158
158
 
@@ -162,7 +162,7 @@ If the spec doesn't set `authenticated_surface`, infer it: scan the description
162
162
 
163
163
  #### S10 — Repository section, single-repo scope
164
164
 
165
- When `issue_type ∈ {Bug, Task, Sub-task, Improvement}` — or a **build-ready childless Story/Spike** (a claimable leaf per `leaf-only-lifecycle`) — description must contain `h2. Repository` naming exactly one repo. Multiple repos OR cross-repo references in AC: FAIL with recommendation `"Split into per-repo work units under a shared parent Story (see lisa:task-decomposition step 1.5)"`.
165
+ When `issue_type ∈ {Bug, Task, Sub-task, Improvement}` — or a **build-ready childless Story/Spike** (a claimable leaf per `leaf-only-lifecycle`) — description must contain a `Repository` section (`h2.` / `##` in proposed text, or an ADF heading in live JIRA) naming exactly one repo. Multiple repos OR cross-repo references in AC: FAIL with recommendation `"Split into per-repo work units under a shared parent Story (see lisa:task-decomposition step 1.5)"`.
166
166
 
167
167
  An **Epic**, or a **Story/Spike that still holds child work** (or is not build-ready): skipped (may span repos — coordination containers, not claimable leaf work units).
168
168
 
@@ -170,7 +170,7 @@ This gate is `product_relevant: false` because cross-repo work units are not a p
170
170
 
171
171
  #### S11 — Validation Journey present
172
172
 
173
- When `runtime_behavior_change = true`, description must contain `h2. Validation Journey`. Skipped for doc-only / config-only / type-only / Epic.
173
+ When `runtime_behavior_change = true`, description must contain a `Validation Journey` section (`h2.` / `##` in proposed text, or an ADF heading in live JIRA). Skipped for doc-only / config-only / type-only / Epic.
174
174
 
175
175
  The caller controls the strictness by passing `journey_followup: "auto"` or `journey_followup: "none"` in the spec:
176
176
  - `auto` (default): if the section is absent, return `FAIL` with remediation `"Invoke lisa:jira-add-journey to append the section after create"`. Callers like `lisa:jira-write-ticket` know to chain `lisa:jira-add-journey` automatically, so this counts as a fixable failure they can resolve in-line — they re-run validation after appending.
@@ -248,7 +248,7 @@ Use the same project-issue-type-metadata lookup from F1 (via `lisa:atlassian-acc
248
248
 
249
249
  ## Execution
250
250
 
251
- 1. Parse `$ARGUMENTS`. If it's a ticket key, fetch the ticket via `lisa:atlassian-access` `operation: read-ticket` and derive the spec from the fetched fields — including `build_ready` (label set contains `status:ready`) and `child_refs` (sub-tasks plus `is blocked by` parentage, resolved as in `lisa:jira-read-ticket`) so S15 can classify the ticket. Otherwise parse the YAML spec.
251
+ 1. Parse `$ARGUMENTS`. If it's a ticket key, fetch the ticket via `lisa:atlassian-access` `operation: read-ticket` and derive the spec from the fetched fields — including `build_ready` (label set contains `status:ready`) and `child_refs` (sub-tasks plus `is blocked by` parentage, resolved as in `lisa:jira-read-ticket`) so S15 can classify the ticket. When the fetched description is ADF, walk the document tree and extract section headings from ADF `heading` nodes, then collect the text between heading nodes for section-specific gates. If the fetched description is a single paragraph containing literal Markdown/wiki heading markers, treat that as a formatting failure rather than accepting substring matches. Otherwise parse the YAML spec.
252
252
  2. If any feasibility gate will run, invoke `lisa:atlassian-access` `operation: list-sites` once to confirm the configured site is reachable (it enforces connection match against `.lisa.config.json`).
253
253
  3. Run every Specification gate in order. Collect PASS / FAIL / N/A with a one-line reason.
254
254
  4. Unless the caller passed `--spec-only` (dry-run), run every Feasibility gate. Collect results.
@@ -37,7 +37,7 @@ Required fields (stop and ask if missing — do not invent values):
37
37
  | Validation Journey | Runtime-behavior changes | Delegate to `/jira-add-journey` |
38
38
  | Target backend environment | Runtime-behavior changes | `dev` / `staging` / `prod`; recorded in description (Phase 3). Skip only for doc/config/type-only tickets. |
39
39
  | Sign-in account / credentials | Tickets that touch authenticated surfaces | Name the account (or source — 1Password item, env var, seeded fixture) and role; recorded in description (Phase 3). Omit when sign-in is not required. |
40
- | Single-repo scope | Bug, Task, Sub-task | These types MUST cover one repo only. If the work crosses repos, split it before creating. Epic / Spike / Story may span repos. |
40
+ | Single-repo scope | Bug, Task, Sub-task, Improvement | These leaf work units MUST cover one repo only. If the work crosses repos, split it before creating. Epic / Spike / Story may span repos. |
41
41
 
42
42
  Optional but recommended: assignee, components, fix versions, labels, sprint, story points, reporter.
43
43
 
@@ -82,10 +82,10 @@ h2. Sign-in Required
82
82
  means "no sign-in needed for this ticket."]
83
83
 
84
84
  h2. Repository
85
- [Required for Bug / Task / Sub-task. Name the single repo this ticket covers.
86
- If the work spans repos, this ticket type is wrong — split into per-repo
87
- Tasks/Subtasks under a parent Story or Epic. Epic / Spike / Story may
88
- list multiple repos.]
85
+ [Required for Bug / Task / Sub-task / Improvement. Name the single repo this ticket covers.
86
+ If the work spans repos, this ticket type is wrong — split into per-repo
87
+ Tasks/Subtasks under a parent Story or Epic. Epic / Spike / Story may
88
+ list multiple repos.]
89
89
 
90
90
  h2. Validation Journey
91
91
  [Delegate to /jira-add-journey if the ticket changes runtime behavior.
@@ -222,6 +222,8 @@ If the validator reports `PASS`, continue to Phase 6.
222
222
 
223
223
  ## Phase 6 — Create or Update
224
224
 
225
+ Before calling `lisa:atlassian-access`, keep the description in Lisa's normal Markdown/wiki-heading authoring shape; the access layer owns conversion through `scripts/markdown-to-adf.mjs` and writes ADF to JIRA. This is required because acli stores raw Markdown/wiki text as one literal paragraph when no ADF object is provided. Post-write verification must confirm the live description contains ADF `heading` nodes for the required sections, not literal `##` / `h2.` text in a paragraph.
226
+
225
227
  ### CREATE
226
228
 
227
229
  1. Invoke `lisa:atlassian-access` via the Skill tool with `operation: write-ticket payload: {...}` containing all Phase 2/3/5 fields and the epic parent from Phase 4a (CREATE form — no existing key).
@@ -239,7 +241,7 @@ If the validator reports `PASS`, continue to Phase 6.
239
241
 
240
242
  ## Phase 7 — Verify
241
243
 
242
- Call the `lisa:jira-verify` skill on the resulting ticket. `lisa:jira-verify` fetches the live ticket and runs `lisa:jira-validate-ticket` against it — same gates as Phase 5.5, but applied to what JIRA actually stored (catches anything dropped or reformatted on write). If it reports failures, fix them before returning. Do not report success on a ticket that fails verify.
244
+ Call the `lisa:jira-verify` skill on the resulting ticket. `lisa:jira-verify` fetches the live ticket and runs `lisa:jira-validate-ticket` against it — same gates as Phase 5.5, but applied to what JIRA actually stored (catches anything dropped or reformatted on write, including Markdown/wiki descriptions that degraded into one literal text paragraph instead of ADF heading nodes). If it reports failures, fix them before returning. Do not report success on a ticket that fails verify.
243
245
 
244
246
  ## Phase 8 — Announce
245
247
 
@@ -255,7 +257,7 @@ Skip this step only on UPDATE when no material change was made.
255
257
 
256
258
  - Never create a non-bug ticket without an epic parent.
257
259
  - Never skip relationship discovery — both the git history search AND the JQL search must run, and their outcomes must be recorded on the ticket. "None found" is acceptable only when it's documented.
258
- - Never create a Bug, Task, or Sub-task that spans multiple repos. Split it before creating.
260
+ - Never create a Bug, Task, Sub-task, or Improvement that spans multiple repos. Split it before creating.
259
261
  - Never include a runtime-behavior ticket without a target backend environment, and never include an authenticated-surface ticket without sign-in credentials in the description.
260
262
  - Never invent custom field values. If the project requires a field you don't have, stop and ask.
261
263
  - Never overwrite a description without reading the current version first.
@@ -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