@ai-content-space/loopx 0.2.4 → 0.2.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +106 -10
- package/README.zh-CN.md +106 -10
- package/docs/loopx/design/finish/345/255/246/344/271/240/345/256/241/350/256/241/351/234/200/346/261/202/350/256/276/350/256/241/346/226/207/346/241/243.md +707 -0
- package/docs/loopx/memory/2026-06-09-stale-archive-hook-guidance.md +15 -0
- package/docs/loopx/memory/README.md +25 -0
- package/docs/loopx/plans/2026-06-08-finish-audit-change-window.md +933 -0
- package/docs/loopx/plans/2026-06-08-finish-learning-audit.md +410 -0
- package/docs/loopx/plans/2026-06-09-cli-onboarding-install-surface.md +1277 -0
- package/docs/loopx/specs/installation.md +33 -0
- package/package.json +18 -2
- package/plugins/loopx/.codex-plugin/plugin.json +1 -1
- package/plugins/loopx/skills/clarify/SKILL.md +1 -1
- package/plugins/loopx/skills/debug/SKILL.md +1 -1
- package/plugins/loopx/skills/doc-readability/SKILL.md +1 -1
- package/plugins/loopx/skills/exec/SKILL.md +11 -1
- package/plugins/loopx/skills/final-review/SKILL.md +1 -1
- package/plugins/loopx/skills/finish/SKILL.md +39 -7
- package/plugins/loopx/skills/fix-review/SKILL.md +1 -1
- package/plugins/loopx/skills/go-style/SKILL.md +1 -1
- package/plugins/loopx/skills/kratos/SKILL.md +1 -1
- package/plugins/loopx/skills/plan/SKILL.md +1 -1
- package/plugins/loopx/skills/refactor-plan/SKILL.md +1 -1
- package/plugins/loopx/skills/review/SKILL.md +1 -1
- package/plugins/loopx/skills/spec/SKILL.md +1 -1
- package/plugins/loopx/skills/subagent-exec/SKILL.md +13 -1
- package/plugins/loopx/skills/tdd/SKILL.md +1 -1
- package/plugins/loopx/skills/verify/SKILL.md +1 -1
- package/scripts/claude-workflow-hook.mjs +50 -1
- package/scripts/codex-workflow-hook.mjs +33 -12
- package/scripts/install-skills.mjs +58 -3
- package/scripts/verify-skills.mjs +83 -7
- package/skills/clarify/SKILL.md +1 -1
- package/skills/debug/SKILL.md +1 -1
- package/skills/doc-readability/SKILL.md +1 -1
- package/skills/exec/SKILL.md +11 -1
- package/skills/final-review/SKILL.md +1 -1
- package/skills/finish/SKILL.md +39 -7
- package/skills/fix-review/SKILL.md +1 -1
- package/skills/go-style/SKILL.md +1 -1
- package/skills/kratos/SKILL.md +1 -1
- package/skills/plan/SKILL.md +1 -1
- package/skills/refactor-plan/SKILL.md +1 -1
- package/skills/review/SKILL.md +1 -1
- package/skills/spec/SKILL.md +1 -1
- package/skills/subagent-exec/SKILL.md +13 -1
- package/skills/tdd/SKILL.md +1 -1
- package/skills/verify/SKILL.md +1 -1
- package/src/cli.mjs +473 -86
- package/src/finish-runtime.mjs +1184 -0
- package/src/install-discovery.mjs +37 -0
- package/src/next-skill.mjs +8 -10
- package/src/workflow.mjs +19 -26
- package/skills/deepsearch/SKILL.md +0 -38
|
@@ -0,0 +1,410 @@
|
|
|
1
|
+
# finish 学习审计 Implementation Plan
|
|
2
|
+
|
|
3
|
+
> **For agentic workers:** REQUIRED SUB-SKILL: Use loopx:subagent-exec (recommended) or loopx:exec to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
|
|
4
|
+
|
|
5
|
+
**Source:** [docs/loopx/design/finish学习审计需求设计文档.md](../design/finish学习审计需求设计文档.md)
|
|
6
|
+
|
|
7
|
+
**Goal:** Add a local finish audit ledger and choice recorder so finish always leaves explainable learning evidence, rejected-candidate reasons, and a persisted user completion choice.
|
|
8
|
+
|
|
9
|
+
**Architecture:** Keep dangerous completion actions in the skill flow. Add a small runtime module that creates `.loopx/finish/<audit-id>/finish-state.json` and `finish-report.md`, plus a record command that updates the same audit directory with the final action/status. The finish skill and its plugin mirror become stricter about running audit first, surfacing candidates and reasons, and recording the final choice. Repo-tracked spec candidates remain `docs/loopx/specs/<domain>.md`.
|
|
10
|
+
|
|
11
|
+
**Tech Stack:** Node.js ESM, `node:test`, `node:fs/promises`, existing loopx workflow and skill governance tests.
|
|
12
|
+
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
### Task 1: Add finish audit runtime primitives
|
|
16
|
+
|
|
17
|
+
**Files:**
|
|
18
|
+
- Create: `src/finish-runtime.mjs`
|
|
19
|
+
- Test: `test/trellis-hardening.test.mjs`
|
|
20
|
+
|
|
21
|
+
- [ ] **Step 1: Write the failing test**
|
|
22
|
+
|
|
23
|
+
```js
|
|
24
|
+
import { mkdtemp, readFile } from 'node:fs/promises';
|
|
25
|
+
import { tmpdir } from 'node:os';
|
|
26
|
+
import { join } from 'node:path';
|
|
27
|
+
import { strict as assert } from 'node:assert';
|
|
28
|
+
import { execFile } from 'node:child_process';
|
|
29
|
+
import { describe, it } from 'node:test';
|
|
30
|
+
import { promisify } from 'node:util';
|
|
31
|
+
|
|
32
|
+
import { finishAuditStage } from '../src/finish-runtime.mjs';
|
|
33
|
+
|
|
34
|
+
const execFileAsync = promisify(execFile);
|
|
35
|
+
|
|
36
|
+
it('creates a finish audit directory with state and report files', async () => {
|
|
37
|
+
const wd = await mkdtemp(join(tmpdir(), 'loopx-finish-audit-'));
|
|
38
|
+
await execFileAsync('git', ['init'], { cwd: wd });
|
|
39
|
+
const result = await finishAuditStage(wd, 'finish-audit-flow');
|
|
40
|
+
|
|
41
|
+
assert.match(result.auditId, /^\d{8}T\d{6}Z-finish-audit-flow$/);
|
|
42
|
+
assert.equal(result.state.status, 'needs-agent-audit');
|
|
43
|
+
assert.equal(await readFile(result.statePath, 'utf8').then((text) => text.includes('"audit_id"')), true);
|
|
44
|
+
assert.equal(await readFile(result.reportPath, 'utf8').then((text) => text.includes('Finish Audit')), true);
|
|
45
|
+
});
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
- [ ] **Step 2: Run test to verify it fails**
|
|
49
|
+
|
|
50
|
+
Run: `node --test test/trellis-hardening.test.mjs -t "creates a finish audit directory with state and report files"`
|
|
51
|
+
Expected: FAIL because `src/finish-runtime.mjs` does not exist yet.
|
|
52
|
+
|
|
53
|
+
- [ ] **Step 3: Write minimal implementation**
|
|
54
|
+
|
|
55
|
+
Implement `src/finish-runtime.mjs` with:
|
|
56
|
+
|
|
57
|
+
```js
|
|
58
|
+
export function resolveFinishAuditRoot(cwd) {}
|
|
59
|
+
export function resolveFinishAuditPath(cwd, auditId) {}
|
|
60
|
+
export async function finishAuditStage(cwd, slug, { env = process.env } = {}) {}
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
Required behavior:
|
|
64
|
+
- create `.loopx/finish/<audit-id>/`
|
|
65
|
+
- write `finish-state.json` with `schema_version`, `audit_id`, `slug`, `status`, `inputs.scanned`, `audit`, `choice`
|
|
66
|
+
- write `finish-report.md` with human-readable sections for summary, scanned inputs, accepted/rejected candidates, and next steps
|
|
67
|
+
- collect git branch/base-branch/worktree evidence defensively, but allow `unknown`/`unavailable` fields when the data cannot be read
|
|
68
|
+
- do not write `.loopx/memory` or `docs/loopx/specs` yet
|
|
69
|
+
|
|
70
|
+
- [ ] **Step 4: Run test to verify it passes**
|
|
71
|
+
|
|
72
|
+
Run: `node --test test/trellis-hardening.test.mjs -t "creates a finish audit directory with state and report files"`
|
|
73
|
+
Expected: PASS
|
|
74
|
+
|
|
75
|
+
- [ ] **Step 5: Commit**
|
|
76
|
+
|
|
77
|
+
```bash
|
|
78
|
+
git add src/finish-runtime.mjs test/trellis-hardening.test.mjs
|
|
79
|
+
git commit -m "feat: add finish audit runtime primitives"
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
### Task 2: Add finish choice recording
|
|
83
|
+
|
|
84
|
+
**Files:**
|
|
85
|
+
- Modify: `src/finish-runtime.mjs`
|
|
86
|
+
- Test: `test/trellis-hardening.test.mjs`
|
|
87
|
+
|
|
88
|
+
- [ ] **Step 1: Write the failing test**
|
|
89
|
+
|
|
90
|
+
```js
|
|
91
|
+
import { mkdtemp, readFile, writeFile } from 'node:fs/promises';
|
|
92
|
+
import { tmpdir } from 'node:os';
|
|
93
|
+
import { join } from 'node:path';
|
|
94
|
+
import { strict as assert } from 'node:assert';
|
|
95
|
+
import { execFile } from 'node:child_process';
|
|
96
|
+
import { promisify } from 'node:util';
|
|
97
|
+
|
|
98
|
+
import { finishAuditStage, finishRecordStage } from '../src/finish-runtime.mjs';
|
|
99
|
+
|
|
100
|
+
const execFileAsync = promisify(execFile);
|
|
101
|
+
|
|
102
|
+
it('records a finish choice and refuses done before audit is complete', async () => {
|
|
103
|
+
const wd = await mkdtemp(join(tmpdir(), 'loopx-finish-record-'));
|
|
104
|
+
await execFileAsync('git', ['init'], { cwd: wd });
|
|
105
|
+
const audit = await finishAuditStage(wd, 'finish-record-flow');
|
|
106
|
+
|
|
107
|
+
await assert.rejects(
|
|
108
|
+
() => finishRecordStage(wd, audit.auditId, { action: 'keep', status: 'done', summary: 'Kept as-is.' }),
|
|
109
|
+
/finish_record_audit_incomplete/,
|
|
110
|
+
);
|
|
111
|
+
|
|
112
|
+
const state = JSON.parse(await readFile(audit.statePath, 'utf8'));
|
|
113
|
+
state.status = 'audited';
|
|
114
|
+
state.audit.accepted_candidates = [{
|
|
115
|
+
kind: 'memory',
|
|
116
|
+
type: 'decision',
|
|
117
|
+
domain: 'workflow',
|
|
118
|
+
summary: 'finish records durable learning evidence',
|
|
119
|
+
evidence: ['skills/finish/SKILL.md'],
|
|
120
|
+
future_usefulness: 'Future agents can confirm finish audits were actually recorded.',
|
|
121
|
+
target: '.loopx/memory/entries/finish-record.md',
|
|
122
|
+
confidence: 'high',
|
|
123
|
+
status: 'accepted',
|
|
124
|
+
}];
|
|
125
|
+
await writeFile(audit.statePath, `${JSON.stringify(state, null, 2)}\n`);
|
|
126
|
+
|
|
127
|
+
const completed = await finishRecordStage(wd, audit.auditId, {
|
|
128
|
+
action: 'keep',
|
|
129
|
+
status: 'done',
|
|
130
|
+
summary: 'Kept branch as-is.',
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
assert.equal(completed.state.choice.action, 'keep');
|
|
134
|
+
assert.equal(completed.state.choice.status, 'done');
|
|
135
|
+
});
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
- [ ] **Step 2: Run test to verify it fails**
|
|
139
|
+
|
|
140
|
+
Run: `node --test test/trellis-hardening.test.mjs -t "records a finish choice and refuses done before audit is complete"`
|
|
141
|
+
Expected: FAIL because `finishRecordStage` is not implemented yet.
|
|
142
|
+
|
|
143
|
+
- [ ] **Step 3: Write minimal implementation**
|
|
144
|
+
|
|
145
|
+
Extend `src/finish-runtime.mjs` with:
|
|
146
|
+
|
|
147
|
+
```js
|
|
148
|
+
export async function finishRecordStage(cwd, auditIdOrPath, { action, status, summary = null, url = null, env = process.env } = {}) {}
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
Required behavior:
|
|
152
|
+
- resolve audit dir from ID or direct path
|
|
153
|
+
- validate `action` in `merge|pr|keep|discard`
|
|
154
|
+
- validate `status` in `pending|done|failed|aborted`
|
|
155
|
+
- refuse `status: done` unless audit state is complete enough to be considered audited
|
|
156
|
+
- update `choice`, `choice_history`, `updated_at`, and `finish-report.md`
|
|
157
|
+
- preserve previous choice entries when the action changes
|
|
158
|
+
|
|
159
|
+
- [ ] **Step 4: Run test to verify it passes**
|
|
160
|
+
|
|
161
|
+
Run: `node --test test/trellis-hardening.test.mjs -t "records a finish choice and refuses done before audit is complete"`
|
|
162
|
+
Expected: PASS
|
|
163
|
+
|
|
164
|
+
- [ ] **Step 5: Commit**
|
|
165
|
+
|
|
166
|
+
```bash
|
|
167
|
+
git add src/finish-runtime.mjs test/trellis-hardening.test.mjs
|
|
168
|
+
git commit -m "feat: record finish completion choices"
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
### Task 3: Wire finish commands into the CLI
|
|
172
|
+
|
|
173
|
+
**Files:**
|
|
174
|
+
- Modify: `src/cli.mjs`
|
|
175
|
+
- Modify: `src/finish-runtime.mjs`
|
|
176
|
+
- Test: `test/trellis-hardening.test.mjs`
|
|
177
|
+
|
|
178
|
+
- [ ] **Step 1: Write the failing test**
|
|
179
|
+
|
|
180
|
+
```js
|
|
181
|
+
import { mkdtemp } from 'node:fs/promises';
|
|
182
|
+
import { tmpdir } from 'node:os';
|
|
183
|
+
import { join } from 'node:path';
|
|
184
|
+
import { execFile } from 'node:child_process';
|
|
185
|
+
import { promisify } from 'node:util';
|
|
186
|
+
|
|
187
|
+
const execFileAsync = promisify(execFile);
|
|
188
|
+
|
|
189
|
+
it('exposes finish-audit and finish-record CLI commands', async () => {
|
|
190
|
+
const wd = await mkdtemp(join(tmpdir(), 'loopx-finish-cli-'));
|
|
191
|
+
await execFileAsync('git', ['init'], { cwd: wd });
|
|
192
|
+
|
|
193
|
+
const audit = await execFileAsync(process.execPath, [cliPath, 'finish-audit', 'finish-cli-flow'], { cwd: wd });
|
|
194
|
+
assert.match(audit.stdout, /audit_id/);
|
|
195
|
+
assert.match(audit.stdout, /finish-report\.md/);
|
|
196
|
+
|
|
197
|
+
const auditId = JSON.parse(audit.stdout).audit_id;
|
|
198
|
+
const record = await execFileAsync(process.execPath, [
|
|
199
|
+
cliPath,
|
|
200
|
+
'finish-record',
|
|
201
|
+
auditId,
|
|
202
|
+
'--action',
|
|
203
|
+
'keep',
|
|
204
|
+
'--status',
|
|
205
|
+
'pending',
|
|
206
|
+
'--summary',
|
|
207
|
+
'Kept branch as-is.',
|
|
208
|
+
], { cwd: wd });
|
|
209
|
+
assert.match(record.stdout, /choice/);
|
|
210
|
+
});
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
- [ ] **Step 2: Run test to verify it fails**
|
|
214
|
+
|
|
215
|
+
Run: `node --test test/trellis-hardening.test.mjs -t "exposes finish-audit and finish-record CLI commands"`
|
|
216
|
+
Expected: FAIL because `src/cli.mjs` does not route these commands yet.
|
|
217
|
+
|
|
218
|
+
- [ ] **Step 3: Write minimal implementation**
|
|
219
|
+
|
|
220
|
+
Update `src/cli.mjs`:
|
|
221
|
+
- import `finishAuditStage` and `finishRecordStage`
|
|
222
|
+
- add `loopx finish-audit [slug] [--json]`
|
|
223
|
+
- add `loopx finish-record <audit-id-or-path> --action ... --status ... [--summary ...] [--url ...]`
|
|
224
|
+
- keep JSON output consistent with other commands
|
|
225
|
+
- return stable error codes via `process.exitCode = 1` on validation failure
|
|
226
|
+
- keep `finish-record` focused on choice persistence; do not add a third finish command
|
|
227
|
+
|
|
228
|
+
- [ ] **Step 4: Run test to verify it passes**
|
|
229
|
+
|
|
230
|
+
Run: `node --test test/trellis-hardening.test.mjs -t "exposes finish-audit and finish-record CLI commands"`
|
|
231
|
+
Expected: PASS
|
|
232
|
+
|
|
233
|
+
- [ ] **Step 5: Commit**
|
|
234
|
+
|
|
235
|
+
```bash
|
|
236
|
+
git add src/cli.mjs src/finish-runtime.mjs test/trellis-hardening.test.mjs
|
|
237
|
+
git commit -m "feat: add finish audit CLI commands"
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
### Task 4: Tighten finish skill docs and plugin mirror
|
|
241
|
+
|
|
242
|
+
**Files:**
|
|
243
|
+
- Modify: `skills/finish/SKILL.md`
|
|
244
|
+
- Modify: `plugins/loopx/skills/finish/SKILL.md`
|
|
245
|
+
- Test: `test/skill-governance.test.mjs`
|
|
246
|
+
|
|
247
|
+
- [ ] **Step 1: Write the failing test**
|
|
248
|
+
|
|
249
|
+
```js
|
|
250
|
+
it('requires finish audit and choice recording in the finish skill docs', async () => {
|
|
251
|
+
const finish = await readFile(join(repoRoot, 'skills', 'finish', 'SKILL.md'), 'utf8');
|
|
252
|
+
assert.match(finish, /finish-audit/);
|
|
253
|
+
assert.match(finish, /finish-record/);
|
|
254
|
+
assert.match(finish, /no_candidates_reason/);
|
|
255
|
+
assert.match(finish, /rejected candidates/);
|
|
256
|
+
assert.match(finish, /choice recording/);
|
|
257
|
+
});
|
|
258
|
+
```
|
|
259
|
+
|
|
260
|
+
- [ ] **Step 2: Run test to verify it fails**
|
|
261
|
+
|
|
262
|
+
Run: `node --test test/skill-governance.test.mjs -t "requires finish audit and choice recording in the finish skill docs"`
|
|
263
|
+
Expected: FAIL because the current skill text only describes generic learning extraction.
|
|
264
|
+
|
|
265
|
+
- [ ] **Step 3: Write minimal implementation**
|
|
266
|
+
|
|
267
|
+
Update both skill mirrors with:
|
|
268
|
+
- audit-first flow in Step 4
|
|
269
|
+
- explicit mention of `.loopx/finish/<audit-id>/finish-state.json`
|
|
270
|
+
- explicit mention that `none` must include scanned inputs and a reason
|
|
271
|
+
- explicit mention that accepted candidates require evidence and rejected candidates require reasons
|
|
272
|
+
- explicit mention that user completion choice must be persisted through `finish-record`
|
|
273
|
+
- keep wording bounded and operational, no new product scope
|
|
274
|
+
|
|
275
|
+
- [ ] **Step 4: Run test to verify it passes**
|
|
276
|
+
|
|
277
|
+
Run: `node --test test/skill-governance.test.mjs -t "requires finish audit and choice recording in the finish skill docs"`
|
|
278
|
+
Expected: PASS
|
|
279
|
+
|
|
280
|
+
- [ ] **Step 5: Commit**
|
|
281
|
+
|
|
282
|
+
```bash
|
|
283
|
+
git add skills/finish/SKILL.md plugins/loopx/skills/finish/SKILL.md test/skill-governance.test.mjs
|
|
284
|
+
git commit -m "docs: tighten finish learning audit flow"
|
|
285
|
+
```
|
|
286
|
+
|
|
287
|
+
### Task 5: Add README and command-reference coverage
|
|
288
|
+
|
|
289
|
+
**Files:**
|
|
290
|
+
- Modify: `README.md`
|
|
291
|
+
- Modify: `README.zh-CN.md`
|
|
292
|
+
- Test: `scripts/verify-skills.mjs`
|
|
293
|
+
|
|
294
|
+
- [ ] **Step 1: Write the failing test**
|
|
295
|
+
|
|
296
|
+
```js
|
|
297
|
+
it('documents finish audit and record commands in the public docs', async () => {
|
|
298
|
+
const readme = await readFile(join(repoRoot, 'README.md'), 'utf8');
|
|
299
|
+
assert.match(readme, /finish-audit/);
|
|
300
|
+
assert.match(readme, /finish-record/);
|
|
301
|
+
assert.match(readme, /\.loopx\/finish\//);
|
|
302
|
+
});
|
|
303
|
+
```
|
|
304
|
+
|
|
305
|
+
- [ ] **Step 2: Run test to verify it fails**
|
|
306
|
+
|
|
307
|
+
Run: `node scripts/verify-skills.mjs`
|
|
308
|
+
Expected: FAIL until the README references are added.
|
|
309
|
+
|
|
310
|
+
- [ ] **Step 3: Write minimal implementation**
|
|
311
|
+
|
|
312
|
+
Update docs to explain:
|
|
313
|
+
- finish now writes a local audit ledger
|
|
314
|
+
- `none` means audited, but no durable learning candidate
|
|
315
|
+
- choice recording lives in the local finish audit directory
|
|
316
|
+
- repo-tracked spec candidates stay in `docs/loopx/specs/`
|
|
317
|
+
- public docs mention `loopx finish-audit` and `loopx finish-record`
|
|
318
|
+
|
|
319
|
+
- [ ] **Step 4: Run test to verify it passes**
|
|
320
|
+
|
|
321
|
+
Run: `node scripts/verify-skills.mjs`
|
|
322
|
+
Expected: PASS
|
|
323
|
+
|
|
324
|
+
- [ ] **Step 5: Commit**
|
|
325
|
+
|
|
326
|
+
```bash
|
|
327
|
+
git add README.md README.zh-CN.md scripts/verify-skills.mjs
|
|
328
|
+
git commit -m "docs: document finish audit ledger"
|
|
329
|
+
```
|
|
330
|
+
|
|
331
|
+
### Task 6: Add focused regression coverage for none/rejected-candidate behavior
|
|
332
|
+
|
|
333
|
+
**Files:**
|
|
334
|
+
- Modify: `test/trellis-hardening.test.mjs`
|
|
335
|
+
- Modify: `test/skill-governance.test.mjs`
|
|
336
|
+
|
|
337
|
+
- [ ] **Step 1: Write the failing test**
|
|
338
|
+
|
|
339
|
+
```js
|
|
340
|
+
it('keeps finish audit explainable when no memory or spec candidates are accepted', async () => {
|
|
341
|
+
const wd = await mkdtemp(join(tmpdir(), 'loopx-finish-none-'));
|
|
342
|
+
const audit = await finishAuditStage(wd, 'finish-none-flow');
|
|
343
|
+
const state = JSON.parse(await readFile(audit.statePath, 'utf8'));
|
|
344
|
+
|
|
345
|
+
assert.equal(state.audit.accepted_candidates.length, 0);
|
|
346
|
+
assert.equal(typeof state.audit.no_candidates_reason, 'string');
|
|
347
|
+
assert.match(await readFile(audit.reportPath, 'utf8'), /rejected candidates/i);
|
|
348
|
+
});
|
|
349
|
+
```
|
|
350
|
+
|
|
351
|
+
- [ ] **Step 2: Run test to verify it fails**
|
|
352
|
+
|
|
353
|
+
Run: `node --test test/trellis-hardening.test.mjs -t "keeps finish audit explainable when no memory or spec candidates are accepted"`
|
|
354
|
+
Expected: FAIL until the audit report/state includes explicit no-candidate reasoning.
|
|
355
|
+
|
|
356
|
+
- [ ] **Step 3: Write minimal implementation**
|
|
357
|
+
|
|
358
|
+
Ensure `finish-audit` always emits:
|
|
359
|
+
- `inputs.scanned`
|
|
360
|
+
- `audit.accepted_candidates`
|
|
361
|
+
- `audit.rejected_candidates`
|
|
362
|
+
- `audit.no_candidates_reason`
|
|
363
|
+
- a report section that distinguishes accepted, rejected, and none cases
|
|
364
|
+
|
|
365
|
+
- [ ] **Step 4: Run test to verify it passes**
|
|
366
|
+
|
|
367
|
+
Run: `node --test test/trellis-hardening.test.mjs -t "keeps finish audit explainable when no memory or spec candidates are accepted"`
|
|
368
|
+
Expected: PASS
|
|
369
|
+
|
|
370
|
+
- [ ] **Step 5: Commit**
|
|
371
|
+
|
|
372
|
+
```bash
|
|
373
|
+
git add src/finish-runtime.mjs test/trellis-hardening.test.mjs test/skill-governance.test.mjs
|
|
374
|
+
git commit -m "test: cover explainable finish audit none cases"
|
|
375
|
+
```
|
|
376
|
+
|
|
377
|
+
### Task 7: Final verification
|
|
378
|
+
|
|
379
|
+
**Files:**
|
|
380
|
+
- Test suite only unless failures require fixes.
|
|
381
|
+
|
|
382
|
+
- [ ] **Step 1: Run the targeted tests**
|
|
383
|
+
|
|
384
|
+
Run:
|
|
385
|
+
```bash
|
|
386
|
+
node --test test/trellis-hardening.test.mjs
|
|
387
|
+
node --test test/skill-governance.test.mjs
|
|
388
|
+
```
|
|
389
|
+
Expected: PASS
|
|
390
|
+
|
|
391
|
+
- [ ] **Step 2: Run the full suite**
|
|
392
|
+
|
|
393
|
+
Run: `npm test`
|
|
394
|
+
Expected: PASS
|
|
395
|
+
|
|
396
|
+
- [ ] **Step 3: Check packaging and docs alignment**
|
|
397
|
+
|
|
398
|
+
Run:
|
|
399
|
+
```bash
|
|
400
|
+
npm pack --dry-run --json
|
|
401
|
+
node scripts/verify-skills.mjs
|
|
402
|
+
```
|
|
403
|
+
Expected: PASS, with README command references and skill-mirror coverage aligned.
|
|
404
|
+
|
|
405
|
+
- [ ] **Step 4: Commit**
|
|
406
|
+
|
|
407
|
+
```bash
|
|
408
|
+
git add src/finish-runtime.mjs src/cli.mjs README.md README.zh-CN.md skills/finish/SKILL.md plugins/loopx/skills/finish/SKILL.md test/trellis-hardening.test.mjs test/skill-governance.test.mjs scripts/verify-skills.mjs
|
|
409
|
+
git commit -m "feat: finish audit ledger and choice recording"
|
|
410
|
+
```
|