@codyswann/lisa 2.108.0 → 2.110.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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/scripts/project-ideation-idempotency-harness.mjs +47 -4
- package/plugins/lisa/skills/github-write-prd/SKILL.md +39 -2
- package/plugins/lisa/skills/project-ideation/SKILL.md +20 -0
- package/plugins/lisa/skills/project-ideation/examples/idempotency-verification-harness.md +3 -2
- package/plugins/lisa-cdk/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-cdk/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-expo/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-expo/.codex-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-nestjs/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-nestjs/.codex-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-rails/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-rails/.codex-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-wiki/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-wiki/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-wiki/scripts/lint-wiki.mjs +5 -1
- package/plugins/src/base/scripts/project-ideation-idempotency-harness.mjs +47 -4
- package/plugins/src/base/skills/github-write-prd/SKILL.md +39 -2
- package/plugins/src/base/skills/project-ideation/SKILL.md +20 -0
- package/plugins/src/base/skills/project-ideation/examples/idempotency-verification-harness.md +3 -2
- package/plugins/src/wiki/scripts/lint-wiki.mjs +5 -1
package/package.json
CHANGED
|
@@ -82,7 +82,7 @@
|
|
|
82
82
|
"lodash": ">=4.18.1"
|
|
83
83
|
},
|
|
84
84
|
"name": "@codyswann/lisa",
|
|
85
|
-
"version": "2.
|
|
85
|
+
"version": "2.110.0",
|
|
86
86
|
"description": "Claude Code governance framework that applies guardrails, guidance, and automated enforcement to projects",
|
|
87
87
|
"main": "dist/index.js",
|
|
88
88
|
"exports": {
|
|
@@ -5,10 +5,17 @@
|
|
|
5
5
|
* The harness runs a caller-supplied deterministic project-ideation command
|
|
6
6
|
* twice, checks that exactly one open GitHub PRD contains the expected marker,
|
|
7
7
|
* then optionally removes the automation memory file and verifies a third run
|
|
8
|
-
* recreates memory without creating a duplicate PRD.
|
|
8
|
+
* recreates memory without creating a duplicate PRD. The recreated memory entry
|
|
9
|
+
* must include the advisory run fields project-ideation promises to write.
|
|
9
10
|
*/
|
|
10
11
|
|
|
11
|
-
import {
|
|
12
|
+
import {
|
|
13
|
+
existsSync,
|
|
14
|
+
mkdirSync,
|
|
15
|
+
readFileSync,
|
|
16
|
+
renameSync,
|
|
17
|
+
rmSync,
|
|
18
|
+
} from "node:fs";
|
|
12
19
|
import { dirname, resolve } from "node:path";
|
|
13
20
|
import { spawnSync } from "node:child_process";
|
|
14
21
|
|
|
@@ -190,7 +197,7 @@ function queryOpenIssuesByMarker(repo, marker) {
|
|
|
190
197
|
* @param {string} marker
|
|
191
198
|
* @param {string} command
|
|
192
199
|
* @param {string} memoryFile
|
|
193
|
-
* @returns {{ readonly count: number, readonly issue: Record<string, any>, readonly memoryRecreated: boolean }}
|
|
200
|
+
* @returns {{ readonly count: number, readonly issue: Record<string, any>, readonly memoryRecreated: boolean, readonly memoryFieldsRecorded: boolean }}
|
|
194
201
|
*/
|
|
195
202
|
function runMissingMemoryVariant(repo, marker, command, memoryFile) {
|
|
196
203
|
const backup = `${memoryFile}.project-ideation-idempotency-backup`;
|
|
@@ -208,9 +215,27 @@ function runMissingMemoryVariant(repo, marker, command, memoryFile) {
|
|
|
208
215
|
marker,
|
|
209
216
|
"after missing-memory run"
|
|
210
217
|
);
|
|
218
|
+
const memoryRecreated = existsSync(memoryFile);
|
|
219
|
+
const memoryFieldsRecorded =
|
|
220
|
+
memoryRecreated &&
|
|
221
|
+
memoryContainsRunEntry(memoryFile, {
|
|
222
|
+
marker,
|
|
223
|
+
prdUrl: String(result.issue.url),
|
|
224
|
+
});
|
|
225
|
+
|
|
226
|
+
if (!memoryRecreated) {
|
|
227
|
+
fail(`Expected missing-memory run to recreate ${memoryFile}`);
|
|
228
|
+
}
|
|
229
|
+
if (!memoryFieldsRecorded) {
|
|
230
|
+
fail(
|
|
231
|
+
`Expected recreated memory to record marker, PRD URL, outcome, lifecycle_role, and source_agreement`
|
|
232
|
+
);
|
|
233
|
+
}
|
|
234
|
+
|
|
211
235
|
return {
|
|
212
236
|
...result,
|
|
213
|
-
memoryRecreated
|
|
237
|
+
memoryRecreated,
|
|
238
|
+
memoryFieldsRecorded,
|
|
214
239
|
};
|
|
215
240
|
} finally {
|
|
216
241
|
if (existsSync(backup)) {
|
|
@@ -221,6 +246,24 @@ function runMissingMemoryVariant(repo, marker, command, memoryFile) {
|
|
|
221
246
|
}
|
|
222
247
|
}
|
|
223
248
|
|
|
249
|
+
/**
|
|
250
|
+
* @param {string} memoryFile
|
|
251
|
+
* @param {{ readonly marker: string, readonly prdUrl: string }} expected
|
|
252
|
+
* @returns {boolean}
|
|
253
|
+
*/
|
|
254
|
+
function memoryContainsRunEntry(memoryFile, expected) {
|
|
255
|
+
const memory = readFileSync(memoryFile, "utf8");
|
|
256
|
+
const requiredFragments = [
|
|
257
|
+
expected.marker,
|
|
258
|
+
expected.prdUrl,
|
|
259
|
+
"outcome:",
|
|
260
|
+
"lifecycle_role:",
|
|
261
|
+
"source_agreement:",
|
|
262
|
+
];
|
|
263
|
+
|
|
264
|
+
return requiredFragments.every(fragment => memory.includes(fragment));
|
|
265
|
+
}
|
|
266
|
+
|
|
224
267
|
/**
|
|
225
268
|
* @param {string} command
|
|
226
269
|
* @param {readonly string[]} argv
|
|
@@ -63,6 +63,37 @@ the single instance. If the live issue body already contains the canonical manag
|
|
|
63
63
|
section, preserve it verbatim unless the caller intentionally supplied an updated canonical section;
|
|
64
64
|
use the shared `usage-accounting` serializer/merge path rather than hand-editing ledger rows.
|
|
65
65
|
|
|
66
|
+
**Exploratory ideation run ledger (both paths).** When the write was initiated by
|
|
67
|
+
`lisa:project-ideation` or carries a project-ideation marker, persist a managed `## Exploratory
|
|
68
|
+
Ideation Run Ledger` section in the PRD body. Prefer the managed section over a comment so the PRD
|
|
69
|
+
itself remains the operator's source of truth; use a managed comment only if the body cannot be
|
|
70
|
+
updated. Keep one managed section by replacing the content between stable markers:
|
|
71
|
+
|
|
72
|
+
```markdown
|
|
73
|
+
## Exploratory Ideation Run Ledger
|
|
74
|
+
<!-- lisa:exploratory-ideation-run-ledger:start -->
|
|
75
|
+
- timestamp: <ISO-8601 run timestamp>
|
|
76
|
+
- automation_id: <Codex/Claude automation id or unavailable>
|
|
77
|
+
- repo: <org>/<repo>
|
|
78
|
+
- prd_ready: true|false
|
|
79
|
+
- persona_evidence_refs: <comma-separated source refs or unavailable>
|
|
80
|
+
- selected_idea: <selected idea title/key>
|
|
81
|
+
- dedupe_marker: <MARKER>
|
|
82
|
+
- prd_url: <created or reused PRD URL>
|
|
83
|
+
- outcome: created|reused
|
|
84
|
+
- lifecycle_role_after_write: draft|ready|in_review|blocked|ticketed|shipped|verified
|
|
85
|
+
- rejected_overlap_candidates: <issue refs/titles considered and rejected, or none>
|
|
86
|
+
- expected_empirical_verification_artifact: <artifact ref or unavailable>
|
|
87
|
+
<!-- lisa:exploratory-ideation-run-ledger:end -->
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
On CREATE, write a ledger entry with `outcome: created`, the selected marker, the created PRD URL,
|
|
91
|
+
and the lifecycle role applied by this write. On UPDATE/reuse, write `outcome: reused`, preserve the
|
|
92
|
+
same dedupe marker, record the reused PRD URL, and report the lifecycle role that remains after
|
|
93
|
+
reconciliation. If the live PRD has progressed past ready, do not downgrade it while recording the
|
|
94
|
+
reuse ledger; the `lifecycle_role_after_write` value must be the existing progressed role. Preserve
|
|
95
|
+
exactly one PRD lifecycle label in the same pass as the ledger write.
|
|
96
|
+
|
|
66
97
|
**CREATE** (no existing issue):
|
|
67
98
|
|
|
68
99
|
1. Write the marker-normalized PRD body to a temp file.
|
|
@@ -70,7 +101,11 @@ use the shared `usage-accounting` serializer/merge path rather than hand-editing
|
|
|
70
101
|
gh issue create --repo "$ORG/$REPO" --title "$TITLE" --body-file /tmp/prd-body.md --label "$ROLE_LABEL"
|
|
71
102
|
```
|
|
72
103
|
3. Capture the returned issue number/URL.
|
|
73
|
-
4.
|
|
104
|
+
4. Rewrite the PRD body with the managed `## Exploratory Ideation Run Ledger` section populated for
|
|
105
|
+
`outcome: created` when the caller supplied project-ideation ledger inputs, then
|
|
106
|
+
`gh issue edit <n> --body-file /tmp/prd-body.md`. This second write is allowed because the URL is
|
|
107
|
+
not known until after creation.
|
|
108
|
+
5. If `github.projects.v2` is enabled, resolve the created PRD issue node id and invoke
|
|
74
109
|
`lisa:github-project-v2` with `operation: ensure-item` and `content_node_id: <issue-node-id>`.
|
|
75
110
|
- `outcome: disabled` → continue normally.
|
|
76
111
|
- `outcome: added` or `reused` → continue normally; membership is now present.
|
|
@@ -80,7 +115,9 @@ use the shared `usage-accounting` serializer/merge path rather than hand-editing
|
|
|
80
115
|
**UPDATE** (existing issue or `source_ref`):
|
|
81
116
|
|
|
82
117
|
1. `gh issue edit <n> --repo "$ORG/$REPO" --body-file /tmp/prd-body.md` with the **marker-normalized**
|
|
83
|
-
body (regenerate in place; never drop the marker
|
|
118
|
+
body (regenerate in place; never drop the marker, the managed `## Exploratory Ideation Run Ledger`
|
|
119
|
+
section, or an existing managed `## Lisa Usage` section). When the caller supplied
|
|
120
|
+
project-ideation ledger inputs, replace the managed ledger content with an `outcome: reused` entry.
|
|
84
121
|
2. Reconcile the lifecycle label to **exactly one**: add `$ROLE_LABEL`, remove every other label in
|
|
85
122
|
the resolved `${ALL_PRD_LABELS[@]}` set (the config-resolved names — not a hard-coded list) via
|
|
86
123
|
`gh issue edit <n> --add-label / --remove-label`. Never leave a PRD carrying two lifecycle labels.
|
|
@@ -161,6 +161,26 @@ For each idea in the creation set, invoke `/lisa:research` with:
|
|
|
161
161
|
`lisa:prd-source-write`. `project-ideation` never writes to the source directly — it delegates, so
|
|
162
162
|
the PRD source stays switchable per project. Capture each returned PRD ref / URL / role / outcome.
|
|
163
163
|
|
|
164
|
+
### Optional Codex automation memory
|
|
165
|
+
|
|
166
|
+
When the run has a Codex automation id or memory path, maintain a concise local advisory ledger after
|
|
167
|
+
the PRD source write returns. Resolve the memory path in this order:
|
|
168
|
+
|
|
169
|
+
1. explicit `memory_file=<path>` or `automation_memory=<path>` argument, when supplied;
|
|
170
|
+
2. `$CODEX_AUTOMATION_MEMORY`, when set;
|
|
171
|
+
3. `$CODEX_HOME/automations/<automation_id>/memory.md`, when `automation_id=<id>` or
|
|
172
|
+
`$CODEX_AUTOMATION_ID` is available.
|
|
173
|
+
|
|
174
|
+
Create the parent directory and `memory.md` if missing. Write one concise run entry keyed by the
|
|
175
|
+
dedupe marker and run timestamp. The entry must include the marker, PRD URL/ref, outcome
|
|
176
|
+
(`created | reused | updated | blocked`), lifecycle role (`draft | ready | blocked` or the returned
|
|
177
|
+
source role), and `source_agreement` (`github-source-wins`, `memory-created`, `memory-updated`, or
|
|
178
|
+
`memory-missing-runtime`). If memory says one thing but the PRD source search finds a matching open
|
|
179
|
+
PRD, GitHub/source truth wins: reuse the source PRD and update memory rather than creating a
|
|
180
|
+
duplicate. Keep memory advisory only; never use it to override lifecycle labels, source marker
|
|
181
|
+
matches, or the PRD source writer's returned role. Do not store secrets, tokens, full PRD bodies, or
|
|
182
|
+
private source excerpts in memory.
|
|
183
|
+
|
|
164
184
|
### Dedupe marker (stable, never title-based)
|
|
165
185
|
|
|
166
186
|
Each created PRD carries the marker `[lisa-project-ideation] idea=<stable-key>`. Compute
|
|
@@ -43,7 +43,8 @@ The harness performs the acceptance check in three phases:
|
|
|
43
43
|
require exactly one match.
|
|
44
44
|
2. Run the same command again, then require the open marker count to remain one.
|
|
45
45
|
3. Move the automation memory file aside, run the command again, require the marker count to remain
|
|
46
|
-
one, and require the memory file to be recreated
|
|
46
|
+
one, and require the memory file to be recreated with marker, PRD URL, outcome, lifecycle role,
|
|
47
|
+
and source agreement fields. The original memory file is restored at the end.
|
|
47
48
|
|
|
48
49
|
## Expected evidence
|
|
49
50
|
|
|
@@ -53,4 +54,4 @@ Capture the harness JSON output as:
|
|
|
53
54
|
- `[EVIDENCE: memory-recreated-after-rerun]` from the missing-memory variant.
|
|
54
55
|
|
|
55
56
|
The run passes only when all reported counts are `1`, the issue URL is the same across phases, and
|
|
56
|
-
`memoryRecreated`
|
|
57
|
+
`memoryRecreated` and `memoryFieldsRecorded` are `true` when `--memory-file` is supplied.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "lisa-openclaw",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.110.0",
|
|
4
4
|
"description": "Connect staff roles to Telegram or Slack via OpenClaw — facilitator/specialist hub-and-spoke routing and repo-coding topics, for Claude Code and Codex",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Cody Swann"
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "lisa-openclaw",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.110.0",
|
|
4
4
|
"description": "Connect staff roles to Telegram or Slack via OpenClaw — facilitator/specialist hub-and-spoke routing and repo-coding topics, across Claude and Codex.",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Cody Swann"
|
|
@@ -317,4 +317,8 @@ if (asJson) {
|
|
|
317
317
|
);
|
|
318
318
|
}
|
|
319
319
|
|
|
320
|
-
process.exit(
|
|
320
|
+
// Set exitCode instead of calling process.exit() so a large `--json` payload
|
|
321
|
+
// written to a pipe (e.g. captured via spawnSync by verify-migration) is fully
|
|
322
|
+
// flushed before the process exits. process.exit() can truncate async stdout
|
|
323
|
+
// writes at the OS pipe buffer (~64KB), corrupting the JSON for the reader.
|
|
324
|
+
process.exitCode = blocking === 0 ? 0 : 1;
|
|
@@ -5,10 +5,17 @@
|
|
|
5
5
|
* The harness runs a caller-supplied deterministic project-ideation command
|
|
6
6
|
* twice, checks that exactly one open GitHub PRD contains the expected marker,
|
|
7
7
|
* then optionally removes the automation memory file and verifies a third run
|
|
8
|
-
* recreates memory without creating a duplicate PRD.
|
|
8
|
+
* recreates memory without creating a duplicate PRD. The recreated memory entry
|
|
9
|
+
* must include the advisory run fields project-ideation promises to write.
|
|
9
10
|
*/
|
|
10
11
|
|
|
11
|
-
import {
|
|
12
|
+
import {
|
|
13
|
+
existsSync,
|
|
14
|
+
mkdirSync,
|
|
15
|
+
readFileSync,
|
|
16
|
+
renameSync,
|
|
17
|
+
rmSync,
|
|
18
|
+
} from "node:fs";
|
|
12
19
|
import { dirname, resolve } from "node:path";
|
|
13
20
|
import { spawnSync } from "node:child_process";
|
|
14
21
|
|
|
@@ -190,7 +197,7 @@ function queryOpenIssuesByMarker(repo, marker) {
|
|
|
190
197
|
* @param {string} marker
|
|
191
198
|
* @param {string} command
|
|
192
199
|
* @param {string} memoryFile
|
|
193
|
-
* @returns {{ readonly count: number, readonly issue: Record<string, any>, readonly memoryRecreated: boolean }}
|
|
200
|
+
* @returns {{ readonly count: number, readonly issue: Record<string, any>, readonly memoryRecreated: boolean, readonly memoryFieldsRecorded: boolean }}
|
|
194
201
|
*/
|
|
195
202
|
function runMissingMemoryVariant(repo, marker, command, memoryFile) {
|
|
196
203
|
const backup = `${memoryFile}.project-ideation-idempotency-backup`;
|
|
@@ -208,9 +215,27 @@ function runMissingMemoryVariant(repo, marker, command, memoryFile) {
|
|
|
208
215
|
marker,
|
|
209
216
|
"after missing-memory run"
|
|
210
217
|
);
|
|
218
|
+
const memoryRecreated = existsSync(memoryFile);
|
|
219
|
+
const memoryFieldsRecorded =
|
|
220
|
+
memoryRecreated &&
|
|
221
|
+
memoryContainsRunEntry(memoryFile, {
|
|
222
|
+
marker,
|
|
223
|
+
prdUrl: String(result.issue.url),
|
|
224
|
+
});
|
|
225
|
+
|
|
226
|
+
if (!memoryRecreated) {
|
|
227
|
+
fail(`Expected missing-memory run to recreate ${memoryFile}`);
|
|
228
|
+
}
|
|
229
|
+
if (!memoryFieldsRecorded) {
|
|
230
|
+
fail(
|
|
231
|
+
`Expected recreated memory to record marker, PRD URL, outcome, lifecycle_role, and source_agreement`
|
|
232
|
+
);
|
|
233
|
+
}
|
|
234
|
+
|
|
211
235
|
return {
|
|
212
236
|
...result,
|
|
213
|
-
memoryRecreated
|
|
237
|
+
memoryRecreated,
|
|
238
|
+
memoryFieldsRecorded,
|
|
214
239
|
};
|
|
215
240
|
} finally {
|
|
216
241
|
if (existsSync(backup)) {
|
|
@@ -221,6 +246,24 @@ function runMissingMemoryVariant(repo, marker, command, memoryFile) {
|
|
|
221
246
|
}
|
|
222
247
|
}
|
|
223
248
|
|
|
249
|
+
/**
|
|
250
|
+
* @param {string} memoryFile
|
|
251
|
+
* @param {{ readonly marker: string, readonly prdUrl: string }} expected
|
|
252
|
+
* @returns {boolean}
|
|
253
|
+
*/
|
|
254
|
+
function memoryContainsRunEntry(memoryFile, expected) {
|
|
255
|
+
const memory = readFileSync(memoryFile, "utf8");
|
|
256
|
+
const requiredFragments = [
|
|
257
|
+
expected.marker,
|
|
258
|
+
expected.prdUrl,
|
|
259
|
+
"outcome:",
|
|
260
|
+
"lifecycle_role:",
|
|
261
|
+
"source_agreement:",
|
|
262
|
+
];
|
|
263
|
+
|
|
264
|
+
return requiredFragments.every(fragment => memory.includes(fragment));
|
|
265
|
+
}
|
|
266
|
+
|
|
224
267
|
/**
|
|
225
268
|
* @param {string} command
|
|
226
269
|
* @param {readonly string[]} argv
|
|
@@ -63,6 +63,37 @@ the single instance. If the live issue body already contains the canonical manag
|
|
|
63
63
|
section, preserve it verbatim unless the caller intentionally supplied an updated canonical section;
|
|
64
64
|
use the shared `usage-accounting` serializer/merge path rather than hand-editing ledger rows.
|
|
65
65
|
|
|
66
|
+
**Exploratory ideation run ledger (both paths).** When the write was initiated by
|
|
67
|
+
`lisa:project-ideation` or carries a project-ideation marker, persist a managed `## Exploratory
|
|
68
|
+
Ideation Run Ledger` section in the PRD body. Prefer the managed section over a comment so the PRD
|
|
69
|
+
itself remains the operator's source of truth; use a managed comment only if the body cannot be
|
|
70
|
+
updated. Keep one managed section by replacing the content between stable markers:
|
|
71
|
+
|
|
72
|
+
```markdown
|
|
73
|
+
## Exploratory Ideation Run Ledger
|
|
74
|
+
<!-- lisa:exploratory-ideation-run-ledger:start -->
|
|
75
|
+
- timestamp: <ISO-8601 run timestamp>
|
|
76
|
+
- automation_id: <Codex/Claude automation id or unavailable>
|
|
77
|
+
- repo: <org>/<repo>
|
|
78
|
+
- prd_ready: true|false
|
|
79
|
+
- persona_evidence_refs: <comma-separated source refs or unavailable>
|
|
80
|
+
- selected_idea: <selected idea title/key>
|
|
81
|
+
- dedupe_marker: <MARKER>
|
|
82
|
+
- prd_url: <created or reused PRD URL>
|
|
83
|
+
- outcome: created|reused
|
|
84
|
+
- lifecycle_role_after_write: draft|ready|in_review|blocked|ticketed|shipped|verified
|
|
85
|
+
- rejected_overlap_candidates: <issue refs/titles considered and rejected, or none>
|
|
86
|
+
- expected_empirical_verification_artifact: <artifact ref or unavailable>
|
|
87
|
+
<!-- lisa:exploratory-ideation-run-ledger:end -->
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
On CREATE, write a ledger entry with `outcome: created`, the selected marker, the created PRD URL,
|
|
91
|
+
and the lifecycle role applied by this write. On UPDATE/reuse, write `outcome: reused`, preserve the
|
|
92
|
+
same dedupe marker, record the reused PRD URL, and report the lifecycle role that remains after
|
|
93
|
+
reconciliation. If the live PRD has progressed past ready, do not downgrade it while recording the
|
|
94
|
+
reuse ledger; the `lifecycle_role_after_write` value must be the existing progressed role. Preserve
|
|
95
|
+
exactly one PRD lifecycle label in the same pass as the ledger write.
|
|
96
|
+
|
|
66
97
|
**CREATE** (no existing issue):
|
|
67
98
|
|
|
68
99
|
1. Write the marker-normalized PRD body to a temp file.
|
|
@@ -70,7 +101,11 @@ use the shared `usage-accounting` serializer/merge path rather than hand-editing
|
|
|
70
101
|
gh issue create --repo "$ORG/$REPO" --title "$TITLE" --body-file /tmp/prd-body.md --label "$ROLE_LABEL"
|
|
71
102
|
```
|
|
72
103
|
3. Capture the returned issue number/URL.
|
|
73
|
-
4.
|
|
104
|
+
4. Rewrite the PRD body with the managed `## Exploratory Ideation Run Ledger` section populated for
|
|
105
|
+
`outcome: created` when the caller supplied project-ideation ledger inputs, then
|
|
106
|
+
`gh issue edit <n> --body-file /tmp/prd-body.md`. This second write is allowed because the URL is
|
|
107
|
+
not known until after creation.
|
|
108
|
+
5. If `github.projects.v2` is enabled, resolve the created PRD issue node id and invoke
|
|
74
109
|
`lisa:github-project-v2` with `operation: ensure-item` and `content_node_id: <issue-node-id>`.
|
|
75
110
|
- `outcome: disabled` → continue normally.
|
|
76
111
|
- `outcome: added` or `reused` → continue normally; membership is now present.
|
|
@@ -80,7 +115,9 @@ use the shared `usage-accounting` serializer/merge path rather than hand-editing
|
|
|
80
115
|
**UPDATE** (existing issue or `source_ref`):
|
|
81
116
|
|
|
82
117
|
1. `gh issue edit <n> --repo "$ORG/$REPO" --body-file /tmp/prd-body.md` with the **marker-normalized**
|
|
83
|
-
body (regenerate in place; never drop the marker
|
|
118
|
+
body (regenerate in place; never drop the marker, the managed `## Exploratory Ideation Run Ledger`
|
|
119
|
+
section, or an existing managed `## Lisa Usage` section). When the caller supplied
|
|
120
|
+
project-ideation ledger inputs, replace the managed ledger content with an `outcome: reused` entry.
|
|
84
121
|
2. Reconcile the lifecycle label to **exactly one**: add `$ROLE_LABEL`, remove every other label in
|
|
85
122
|
the resolved `${ALL_PRD_LABELS[@]}` set (the config-resolved names — not a hard-coded list) via
|
|
86
123
|
`gh issue edit <n> --add-label / --remove-label`. Never leave a PRD carrying two lifecycle labels.
|
|
@@ -161,6 +161,26 @@ For each idea in the creation set, invoke `/lisa:research` with:
|
|
|
161
161
|
`lisa:prd-source-write`. `project-ideation` never writes to the source directly — it delegates, so
|
|
162
162
|
the PRD source stays switchable per project. Capture each returned PRD ref / URL / role / outcome.
|
|
163
163
|
|
|
164
|
+
### Optional Codex automation memory
|
|
165
|
+
|
|
166
|
+
When the run has a Codex automation id or memory path, maintain a concise local advisory ledger after
|
|
167
|
+
the PRD source write returns. Resolve the memory path in this order:
|
|
168
|
+
|
|
169
|
+
1. explicit `memory_file=<path>` or `automation_memory=<path>` argument, when supplied;
|
|
170
|
+
2. `$CODEX_AUTOMATION_MEMORY`, when set;
|
|
171
|
+
3. `$CODEX_HOME/automations/<automation_id>/memory.md`, when `automation_id=<id>` or
|
|
172
|
+
`$CODEX_AUTOMATION_ID` is available.
|
|
173
|
+
|
|
174
|
+
Create the parent directory and `memory.md` if missing. Write one concise run entry keyed by the
|
|
175
|
+
dedupe marker and run timestamp. The entry must include the marker, PRD URL/ref, outcome
|
|
176
|
+
(`created | reused | updated | blocked`), lifecycle role (`draft | ready | blocked` or the returned
|
|
177
|
+
source role), and `source_agreement` (`github-source-wins`, `memory-created`, `memory-updated`, or
|
|
178
|
+
`memory-missing-runtime`). If memory says one thing but the PRD source search finds a matching open
|
|
179
|
+
PRD, GitHub/source truth wins: reuse the source PRD and update memory rather than creating a
|
|
180
|
+
duplicate. Keep memory advisory only; never use it to override lifecycle labels, source marker
|
|
181
|
+
matches, or the PRD source writer's returned role. Do not store secrets, tokens, full PRD bodies, or
|
|
182
|
+
private source excerpts in memory.
|
|
183
|
+
|
|
164
184
|
### Dedupe marker (stable, never title-based)
|
|
165
185
|
|
|
166
186
|
Each created PRD carries the marker `[lisa-project-ideation] idea=<stable-key>`. Compute
|
package/plugins/src/base/skills/project-ideation/examples/idempotency-verification-harness.md
CHANGED
|
@@ -43,7 +43,8 @@ The harness performs the acceptance check in three phases:
|
|
|
43
43
|
require exactly one match.
|
|
44
44
|
2. Run the same command again, then require the open marker count to remain one.
|
|
45
45
|
3. Move the automation memory file aside, run the command again, require the marker count to remain
|
|
46
|
-
one, and require the memory file to be recreated
|
|
46
|
+
one, and require the memory file to be recreated with marker, PRD URL, outcome, lifecycle role,
|
|
47
|
+
and source agreement fields. The original memory file is restored at the end.
|
|
47
48
|
|
|
48
49
|
## Expected evidence
|
|
49
50
|
|
|
@@ -53,4 +54,4 @@ Capture the harness JSON output as:
|
|
|
53
54
|
- `[EVIDENCE: memory-recreated-after-rerun]` from the missing-memory variant.
|
|
54
55
|
|
|
55
56
|
The run passes only when all reported counts are `1`, the issue URL is the same across phases, and
|
|
56
|
-
`memoryRecreated`
|
|
57
|
+
`memoryRecreated` and `memoryFieldsRecorded` are `true` when `--memory-file` is supplied.
|
|
@@ -317,4 +317,8 @@ if (asJson) {
|
|
|
317
317
|
);
|
|
318
318
|
}
|
|
319
319
|
|
|
320
|
-
process.exit(
|
|
320
|
+
// Set exitCode instead of calling process.exit() so a large `--json` payload
|
|
321
|
+
// written to a pipe (e.g. captured via spawnSync by verify-migration) is fully
|
|
322
|
+
// flushed before the process exits. process.exit() can truncate async stdout
|
|
323
|
+
// writes at the OS pipe buffer (~64KB), corrupting the JSON for the reader.
|
|
324
|
+
process.exitCode = blocking === 0 ? 0 : 1;
|