hadara 0.3.1-rc.1 → 0.3.2
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 +37 -18
- package/dist/cli/evidence-json.js +8 -1
- package/dist/cli/evidence.js +81 -3
- package/dist/cli/init.js +12 -8
- package/dist/evidence/evidence.js +62 -7
- package/dist/evidence/semantics.js +12 -3
- package/dist/schemas/evidence-list.schema.json +22 -3
- package/dist/services/capability-registry.js +10 -6
- package/dist/services/evidence-list.js +24 -6
- package/dist/tui/read-model.js +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -5,8 +5,8 @@
|
|
|
5
5
|
</p>
|
|
6
6
|
|
|
7
7
|
<p align="center">
|
|
8
|
-
<img alt="Stable npm release" src="https://img.shields.io/badge/npm-0.3.
|
|
9
|
-
<img alt="Source version" src="https://img.shields.io/badge/source-0.3.
|
|
8
|
+
<img alt="Stable npm release" src="https://img.shields.io/badge/npm-0.3.2-blue">
|
|
9
|
+
<img alt="Source version" src="https://img.shields.io/badge/source-0.3.2-blue">
|
|
10
10
|
<img alt="Node.js" src="https://img.shields.io/badge/node-%3E%3D22-brightgreen">
|
|
11
11
|
<img alt="License" src="https://img.shields.io/badge/license-MIT-lightgrey">
|
|
12
12
|
</p>
|
|
@@ -24,24 +24,24 @@ This repository is both the HADARA source checkout and the HADARA protocol works
|
|
|
24
24
|
Current stable npm release:
|
|
25
25
|
|
|
26
26
|
```text
|
|
27
|
-
hadara@0.3.
|
|
27
|
+
hadara@0.3.2
|
|
28
28
|
```
|
|
29
29
|
|
|
30
30
|
Current release candidate:
|
|
31
31
|
|
|
32
32
|
```text
|
|
33
|
-
hadara@0.3.
|
|
33
|
+
hadara@0.3.2-rc.0
|
|
34
34
|
```
|
|
35
35
|
|
|
36
|
-
The 0.3.
|
|
36
|
+
The 0.3.2 line is the Evidence v2 refactor release. It hardens evidence writer metadata, exact resolution markers, durable evidence id discovery, canonical/derived evidence boundaries, and release-facing docs after the 0.3.1 post-publish recycle.
|
|
37
37
|
|
|
38
|
-
Phase
|
|
38
|
+
Phase labels are internal implementation phases, not npm release-candidate labels. The stable `0.3.2` source is prepared for approval-gated npm publish; the `0.3.2-rc.0` package remains the prior release-candidate evaluation build until stable publish is executed.
|
|
39
39
|
|
|
40
40
|
| Surface | Status |
|
|
41
41
|
|---|---|
|
|
42
|
-
| Current stable | [`hadara@0.3.
|
|
43
|
-
| Current RC | [`hadara@0.3.
|
|
44
|
-
| Previous RC | [`hadara@0.3.
|
|
42
|
+
| Current stable | [`hadara@0.3.2`](docs/RELEASE_NOTES.md#032) |
|
|
43
|
+
| Current RC | [`hadara@0.3.2-rc.0`](docs/RELEASE_NOTES.md#032-rc0) |
|
|
44
|
+
| Previous RC | [`hadara@0.3.1-rc.1`](docs/RELEASE_NOTES.md#031-rc1) |
|
|
45
45
|
| Historical RCs | See [Release Notes](docs/RELEASE_NOTES.md). |
|
|
46
46
|
| GitHub Release | Secondary target, approval-gated. |
|
|
47
47
|
| Docker image | Deferred. |
|
|
@@ -57,7 +57,7 @@ Requires Node.js 22.
|
|
|
57
57
|
Install the stable release:
|
|
58
58
|
|
|
59
59
|
```bash
|
|
60
|
-
npm install -g hadara@0.3.
|
|
60
|
+
npm install -g hadara@0.3.2
|
|
61
61
|
hadara help
|
|
62
62
|
hadara doctor --json
|
|
63
63
|
```
|
|
@@ -65,26 +65,26 @@ hadara doctor --json
|
|
|
65
65
|
Run without a global install:
|
|
66
66
|
|
|
67
67
|
```bash
|
|
68
|
-
npx hadara@0.3.
|
|
69
|
-
npx hadara@0.3.
|
|
68
|
+
npx hadara@0.3.2 help
|
|
69
|
+
npx hadara@0.3.2 doctor --json
|
|
70
70
|
```
|
|
71
71
|
|
|
72
72
|
Evaluate the release candidate explicitly:
|
|
73
73
|
|
|
74
74
|
```bash
|
|
75
|
-
npm install -g hadara@0.3.
|
|
76
|
-
npx hadara@0.3.
|
|
75
|
+
npm install -g hadara@0.3.2-rc.0
|
|
76
|
+
npx hadara@0.3.2-rc.0 help
|
|
77
77
|
```
|
|
78
78
|
|
|
79
79
|
For release or recycle evidence, prefer an isolated prefix install when PATH, global installs, or `npx` cache behavior may be stale:
|
|
80
80
|
|
|
81
81
|
```bash
|
|
82
82
|
tmp="$(mktemp -d)"
|
|
83
|
-
npm --prefix "$tmp" install hadara@0.3.
|
|
83
|
+
npm --prefix "$tmp" install hadara@0.3.2
|
|
84
84
|
"$tmp/node_modules/.bin/hadara" version --json
|
|
85
85
|
```
|
|
86
86
|
|
|
87
|
-
`npx hadara@0.3.
|
|
87
|
+
`npx hadara@0.3.2 ...` remains convenient for normal use. The isolated installed-bin path is stronger proof that the published package installed and executed from the intended package tree.
|
|
88
88
|
|
|
89
89
|
## What HADARA Gives You
|
|
90
90
|
|
|
@@ -128,7 +128,7 @@ The primary path is intentionally small:
|
|
|
128
128
|
hadara task next --json
|
|
129
129
|
hadara task create "implement a focused change" --json
|
|
130
130
|
hadara task status --task T-XXXX --json
|
|
131
|
-
hadara evidence add-command --task T-XXXX --summary "..." --result passed --idempotency-key "command:T-XXXX:check" --json
|
|
131
|
+
hadara evidence add-command --task T-XXXX --summary "..." --result passed --category validation --idempotency-key "command:T-XXXX:check" --json
|
|
132
132
|
hadara task finish --task T-XXXX --json
|
|
133
133
|
hadara task finish --task T-XXXX --execute --json
|
|
134
134
|
# Finalize Task Capsule docs and tracked state docs before closing.
|
|
@@ -139,6 +139,19 @@ hadara task audit-close --task T-XXXX --json
|
|
|
139
139
|
hadara handoff suggest --task T-XXXX --json
|
|
140
140
|
```
|
|
141
141
|
|
|
142
|
+
When `evidence add-command` uses both legacy `--result` and v2 `--outcome`, matching outcomes must agree with the legacy result. `recorded` and `not-applicable` outcomes keep legacy result `unknown`; incompatible combinations fail before evidence is appended.
|
|
143
|
+
|
|
144
|
+
Use `hadara evidence list --task T-XXXX` to discover evidence ids before writing exact resolution markers. Text output shows `[id] time | category/outcome | visibility | summary`; JSON output includes `id`, `idSource`, `idStability`, `persistedSchemaVersion`, `category`, `outcome`, and `tags`. For long-lived references, copy only durable persisted `ev:` ids:
|
|
145
|
+
|
|
146
|
+
```bash
|
|
147
|
+
hadara evidence list --task T-XXXX
|
|
148
|
+
hadara evidence add-command --task T-XXXX --summary "Fix verified" --result passed --category validation --resolves ev:T-XXXX:aaaaaaaaaaaaaaaaaaaaaaaa --json
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
Legacy compatibility ids are inspection-only and are not the preferred durable reference for `resolves:` or `supersedes:` examples.
|
|
152
|
+
|
|
153
|
+
Deferred Evidence v2 scope is explicit: rebuild preview/execute, `check-id`, `subject`, and a new add-command report schema id are future candidates, not current command behavior. Treat `evidence.jsonl` as canonical append-only evidence and `EVIDENCE.md` as a non-canonical human summary.
|
|
154
|
+
|
|
142
155
|
Optional workflow compression is read-only. Use it separately when you want a compact current-stage report and next recommended action:
|
|
143
156
|
|
|
144
157
|
```bash
|
|
@@ -232,7 +245,7 @@ Dashboard, TUI, Hermes, MCP, installer, package, release, and run commands stay
|
|
|
232
245
|
|
|
233
246
|
## Safety Boundaries
|
|
234
247
|
|
|
235
|
-
HADARA 0.3.
|
|
248
|
+
HADARA 0.3.2 is not:
|
|
236
249
|
|
|
237
250
|
- a full agent runtime;
|
|
238
251
|
- Rack/enterprise behavior;
|
|
@@ -268,6 +281,12 @@ AGENTS.md
|
|
|
268
281
|
|
|
269
282
|
Portable/local state is not committed. Project docs, Task Capsules, and reduced public evidence are committed when they represent reproducible context.
|
|
270
283
|
|
|
284
|
+
### Evidence Rebuild Boundary
|
|
285
|
+
|
|
286
|
+
`evidence.jsonl` is the canonical Task Capsule evidence source. `EVIDENCE.md` is a non-canonical human summary that can help review validation history, but it must not be treated as the source of truth for rebuild, migration, or resolution logic.
|
|
287
|
+
|
|
288
|
+
0.3.2 does not implement `hadara evidence rebuild --json` or an execute mode. Future rebuild work must first define whether a difference is formatting regeneration, managed-section drift, or data inconsistency before reporting `wouldChange`. Any later write-capable rebuild flow must be dry-run-first, reviewed, and before-hash guarded before it rewrites derived Markdown.
|
|
289
|
+
|
|
271
290
|
## Development / Contributing
|
|
272
291
|
|
|
273
292
|
Initialize a project:
|
|
@@ -33,11 +33,18 @@ function createEvidenceCollectReport(projectRoot, input) {
|
|
|
33
33
|
summary: input.summary,
|
|
34
34
|
result: input.result,
|
|
35
35
|
visibility: input.visibility,
|
|
36
|
+
category: input.category,
|
|
37
|
+
outcome: input.outcome,
|
|
38
|
+
tags: input.tags,
|
|
36
39
|
idempotencyKey: input.idempotencyKey
|
|
37
40
|
});
|
|
38
41
|
}
|
|
39
42
|
catch (error) {
|
|
40
|
-
if (error instanceof workspace_1.WorkspaceFileError ||
|
|
43
|
+
if (error instanceof workspace_1.WorkspaceFileError ||
|
|
44
|
+
error instanceof evidence_1.EvidenceArtifactPolicyError ||
|
|
45
|
+
error instanceof evidence_1.EvidenceAppendLockError ||
|
|
46
|
+
error instanceof evidence_1.EvidenceResultOutcomeMismatchError ||
|
|
47
|
+
error instanceof evidence_1.EvidenceTaskDirectoryError) {
|
|
41
48
|
return {
|
|
42
49
|
schemaVersion: 'hadara.evidence.collect.v1',
|
|
43
50
|
command: 'evidence.collect',
|
package/dist/cli/evidence.js
CHANGED
|
@@ -4,6 +4,8 @@ exports.handleEvidenceCommand = handleEvidenceCommand;
|
|
|
4
4
|
exports.parseEvidenceKind = parseEvidenceKind;
|
|
5
5
|
exports.parseEvidenceResult = parseEvidenceResult;
|
|
6
6
|
exports.parseEvidenceVisibility = parseEvidenceVisibility;
|
|
7
|
+
exports.parseEvidenceCategory = parseEvidenceCategory;
|
|
8
|
+
exports.parseEvidenceOutcome = parseEvidenceOutcome;
|
|
7
9
|
const evidence_1 = require("../evidence/evidence");
|
|
8
10
|
const evidence_json_1 = require("./evidence-json");
|
|
9
11
|
const evidence_lint_1 = require("../services/evidence-lint");
|
|
@@ -24,7 +26,7 @@ function handleEvidenceCommand(input) {
|
|
|
24
26
|
}
|
|
25
27
|
else {
|
|
26
28
|
for (const record of report.records) {
|
|
27
|
-
console.log(
|
|
29
|
+
console.log(`[${evidenceListDisplayId(record)}] ${record.time} | ${evidenceListCategory(record)}/${evidenceListOutcome(record)} | ${record.visibility} | ${record.summary}`);
|
|
28
30
|
}
|
|
29
31
|
for (const issue of report.issues) {
|
|
30
32
|
console.log(`[${issue.severity}] ${issue.code}: ${issue.message}`);
|
|
@@ -76,9 +78,30 @@ function handleEvidenceCommand(input) {
|
|
|
76
78
|
if (sub === 'add-command') {
|
|
77
79
|
const taskId = (0, args_1.getRequiredStringOption)(input.args, '--task');
|
|
78
80
|
const summary = (0, args_1.getStringOption)(input.args, '--summary') ?? 'Command completed.';
|
|
79
|
-
const
|
|
81
|
+
const outcome = parseOptionalEvidenceOutcome((0, args_1.getStringOption)(input.args, '--outcome'));
|
|
82
|
+
const explicitResult = (0, args_1.getStringOption)(input.args, '--result');
|
|
83
|
+
const result = parseEvidenceResult(explicitResult ?? outcomeToLegacyResult(outcome));
|
|
84
|
+
const resultOutcomeIssue = (0, evidence_1.validateEvidenceResultOutcomeCompatibility)({ result, outcome });
|
|
85
|
+
if (resultOutcomeIssue) {
|
|
86
|
+
const report = {
|
|
87
|
+
schemaVersion: 'hadara.evidence.collect.v1',
|
|
88
|
+
command: 'evidence.add-command',
|
|
89
|
+
ok: false,
|
|
90
|
+
issues: [resultOutcomeIssue]
|
|
91
|
+
};
|
|
92
|
+
if (input.jsonOutput) {
|
|
93
|
+
console.log(JSON.stringify(report, null, 2));
|
|
94
|
+
}
|
|
95
|
+
else {
|
|
96
|
+
console.log(`[error] ${resultOutcomeIssue.code}: ${resultOutcomeIssue.message}`);
|
|
97
|
+
}
|
|
98
|
+
process.exitCode = 6;
|
|
99
|
+
return true;
|
|
100
|
+
}
|
|
80
101
|
const visibility = parseEvidenceVisibility((0, args_1.getStringOption)(input.args, '--visibility', 'public') ?? 'public', (0, args_1.getFlag)(input.args, '--private'));
|
|
81
102
|
const idempotencyKey = (0, args_1.getStringOption)(input.args, '--idempotency-key');
|
|
103
|
+
const category = parseOptionalEvidenceCategory((0, args_1.getStringOption)(input.args, '--category'));
|
|
104
|
+
const tags = resolutionTagsFromArgs(input.args);
|
|
82
105
|
if (input.jsonOutput) {
|
|
83
106
|
const report = (0, evidence_json_1.createEvidenceCollectReport)(input.projectRoot, {
|
|
84
107
|
taskId,
|
|
@@ -86,6 +109,9 @@ function handleEvidenceCommand(input) {
|
|
|
86
109
|
summary,
|
|
87
110
|
result,
|
|
88
111
|
visibility,
|
|
112
|
+
category,
|
|
113
|
+
outcome,
|
|
114
|
+
tags,
|
|
89
115
|
idempotencyKey
|
|
90
116
|
});
|
|
91
117
|
console.log(JSON.stringify({ ...report, command: 'evidence.add-command' }, null, 2));
|
|
@@ -93,7 +119,7 @@ function handleEvidenceCommand(input) {
|
|
|
93
119
|
process.exitCode = 6;
|
|
94
120
|
}
|
|
95
121
|
else {
|
|
96
|
-
const appendResult = (0, evidence_1.appendEvidenceWithResult)(input.projectRoot, { taskId, kind: 'command-log', summary, result, visibility, idempotencyKey });
|
|
122
|
+
const appendResult = (0, evidence_1.appendEvidenceWithResult)(input.projectRoot, { taskId, kind: 'command-log', summary, result, visibility, category, outcome, tags, idempotencyKey });
|
|
97
123
|
if (appendResult.existing) {
|
|
98
124
|
console.log(`[HADARA] Command evidence already exists: ${persistedEvidenceId(appendResult.evidence)}`);
|
|
99
125
|
}
|
|
@@ -140,6 +166,15 @@ function handleEvidenceCommand(input) {
|
|
|
140
166
|
function persistedEvidenceId(record) {
|
|
141
167
|
return record.schemaVersion === 'hadara.evidence.v2' && record.id ? record.id : 'evidence.jsonl';
|
|
142
168
|
}
|
|
169
|
+
function evidenceListDisplayId(record) {
|
|
170
|
+
return record.id;
|
|
171
|
+
}
|
|
172
|
+
function evidenceListCategory(record) {
|
|
173
|
+
return record.category;
|
|
174
|
+
}
|
|
175
|
+
function evidenceListOutcome(record) {
|
|
176
|
+
return record.outcome;
|
|
177
|
+
}
|
|
143
178
|
function parseEvidenceKind(value) {
|
|
144
179
|
if (['test-log', 'command-log', 'diff-summary', 'screenshot', 'note'].includes(value)) {
|
|
145
180
|
return value;
|
|
@@ -159,3 +194,46 @@ function parseEvidenceVisibility(value, privateFlag = false) {
|
|
|
159
194
|
return value;
|
|
160
195
|
throw new Error(`unsupported evidence visibility: ${value}`);
|
|
161
196
|
}
|
|
197
|
+
function parseEvidenceCategory(value) {
|
|
198
|
+
if (['validation', 'implementation', 'release', 'security', 'policy', 'operation', 'decision', 'handoff', 'audit', 'note', 'observation'].includes(value)) {
|
|
199
|
+
return value;
|
|
200
|
+
}
|
|
201
|
+
throw new Error(`unsupported evidence category: ${value}`);
|
|
202
|
+
}
|
|
203
|
+
function parseEvidenceOutcome(value) {
|
|
204
|
+
if (value === 'passed' || value === 'failed' || value === 'blocked' || value === 'unknown' || value === 'recorded' || value === 'not-applicable') {
|
|
205
|
+
return value;
|
|
206
|
+
}
|
|
207
|
+
throw new Error(`unsupported evidence outcome: ${value}`);
|
|
208
|
+
}
|
|
209
|
+
function parseOptionalEvidenceCategory(value) {
|
|
210
|
+
return value ? parseEvidenceCategory(value) : undefined;
|
|
211
|
+
}
|
|
212
|
+
function parseOptionalEvidenceOutcome(value) {
|
|
213
|
+
return value ? parseEvidenceOutcome(value) : undefined;
|
|
214
|
+
}
|
|
215
|
+
function outcomeToLegacyResult(outcome) {
|
|
216
|
+
if (outcome === 'passed' || outcome === 'failed' || outcome === 'blocked' || outcome === 'unknown')
|
|
217
|
+
return outcome;
|
|
218
|
+
return 'unknown';
|
|
219
|
+
}
|
|
220
|
+
function resolutionTagsFromArgs(args) {
|
|
221
|
+
const tags = [];
|
|
222
|
+
for (const id of getRepeatedStringOptions(args, '--resolves'))
|
|
223
|
+
tags.push(`resolves:${id}`);
|
|
224
|
+
for (const id of getRepeatedStringOptions(args, '--supersedes'))
|
|
225
|
+
tags.push(`supersedes:${id}`);
|
|
226
|
+
return tags.length > 0 ? tags : undefined;
|
|
227
|
+
}
|
|
228
|
+
function getRepeatedStringOptions(args, option) {
|
|
229
|
+
const values = [];
|
|
230
|
+
for (let index = 0; index < args.length; index += 1) {
|
|
231
|
+
if (args[index] !== option)
|
|
232
|
+
continue;
|
|
233
|
+
const value = args[index + 1];
|
|
234
|
+
if (!value || value.startsWith('--'))
|
|
235
|
+
throw new Error(`missing value for ${option}`);
|
|
236
|
+
values.push(value);
|
|
237
|
+
}
|
|
238
|
+
return values;
|
|
239
|
+
}
|
package/dist/cli/init.js
CHANGED
|
@@ -1118,7 +1118,7 @@ hadara task status --task T-XXXX --json
|
|
|
1118
1118
|
|
|
1119
1119
|
# Do the scoped work.
|
|
1120
1120
|
|
|
1121
|
-
hadara evidence add-command --task T-XXXX --summary "..." --result passed --idempotency-key "command:T-XXXX:check" --json
|
|
1121
|
+
hadara evidence add-command --task T-XXXX --summary "..." --result passed --category validation --idempotency-key "command:T-XXXX:check" --json
|
|
1122
1122
|
|
|
1123
1123
|
hadara task finish --task T-XXXX --json
|
|
1124
1124
|
hadara task finish --task T-XXXX --execute --json
|
|
@@ -1140,7 +1140,7 @@ hadara task audit-close --task T-XXXX --json
|
|
|
1140
1140
|
|---|---|---|
|
|
1141
1141
|
| \`task next\` | Read-only | Recommends work; does not create tasks. |
|
|
1142
1142
|
| \`task status\` | Read-only | \`ok\` means report generation succeeded; readiness is in \`state.ready\`, \`summary.blockers\`, and \`issues\`. |
|
|
1143
|
-
| \`evidence add-command\` | Write | Appends command-log evidence; does not execute shell commands; optional \`--idempotency-key\` prevents duplicate same-key records. |
|
|
1143
|
+
| \`evidence add-command\` | Write | Appends command-log evidence; does not execute shell commands; optional \`--category\`/\`--outcome\`/\`--resolves\`/\`--supersedes\` enrich v2 metadata, result/outcome mismatches are rejected, and optional \`--idempotency-key\` prevents duplicate same-key records. |
|
|
1144
1144
|
| \`task ready\` | Read-only | Checks readiness; does not mutate evidence or status docs. |
|
|
1145
1145
|
| \`task finish\` | Dry-run by default; writes only with \`--execute\` | Bounded to \`TASK.md\` and \`docs/TASK_BOARD.md\`. |
|
|
1146
1146
|
| \`task close\` | Dry-run by default; writes only with \`--execute\` | Bounded to close evidence append. |
|
|
@@ -1165,8 +1165,10 @@ Before running \`task ready\` and \`task close\`, finish all close-source edits:
|
|
|
1165
1165
|
1. Do not hand-edit Task Capsule \`evidence.jsonl\`.
|
|
1166
1166
|
2. Append evidence through HADARA commands so schema, visibility, and artifact-safety checks run consistently.
|
|
1167
1167
|
3. Record failed or blocked checks honestly. Do not replace them later with optimistic summaries; add newer evidence that explains the fix or residual risk.
|
|
1168
|
-
4. Use \`hadara evidence
|
|
1169
|
-
5. Use \`hadara evidence
|
|
1168
|
+
4. Use \`hadara evidence list --task <task-id>\` to discover evidence ids and copy durable persisted \`ev:\` ids for long-lived \`--resolves\` or \`--supersedes\` references. Legacy compatibility ids are inspection-only and are not the preferred durable reference.
|
|
1169
|
+
5. Use \`hadara evidence add-command --task <task-id> --summary <text> --result passed|failed|blocked|unknown --json\` for command results when no artifact file is attached. Add \`--category <category>\` or \`--outcome <outcome>\` when summary heuristics are not precise enough; if both \`--result\` and \`--outcome\` are supplied, matching outcomes must match the legacy result, while \`recorded\` and \`not-applicable\` require \`--result unknown\` or no explicit result. Use \`--resolves <ev:evidence-id>\` or \`--supersedes <ev:evidence-id>\` for exact v2 resolution markers from passed or recorded follow-up evidence, and \`--idempotency-key <key>\` when rerunning the same logical check should report one durable evidence identity instead of appending duplicates.
|
|
1170
|
+
6. Use \`hadara evidence lint --task <task-id> --json\` when evidence drift is suspected or before close if evidence files were touched manually by mistake.
|
|
1171
|
+
7. Treat Task Capsule \`evidence.jsonl\` as canonical append-only evidence and \`EVIDENCE.md\` as a non-canonical human summary. \`hadara evidence rebuild\` is not implemented in this scaffold; future rebuild work must define whether \`wouldChange\` means formatting regeneration, managed-section drift, or data inconsistency before preview semantics are safe, and any execute mode must be dry-run-first and before-hash guarded.
|
|
1170
1172
|
|
|
1171
1173
|
## Session End
|
|
1172
1174
|
|
|
@@ -1336,7 +1338,7 @@ hadara task status --task T-XXXX --json
|
|
|
1336
1338
|
|
|
1337
1339
|
# Do the scoped work.
|
|
1338
1340
|
|
|
1339
|
-
hadara evidence add-command --task T-XXXX --summary "..." --result passed --idempotency-key "command:T-XXXX:check" --json
|
|
1341
|
+
hadara evidence add-command --task T-XXXX --summary "..." --result passed --category validation --idempotency-key "command:T-XXXX:check" --json
|
|
1340
1342
|
|
|
1341
1343
|
hadara task finish --task T-XXXX --json
|
|
1342
1344
|
hadara task finish --task T-XXXX --execute --json
|
|
@@ -1417,7 +1419,7 @@ Evidence outcome tokens are \`passed\`, \`failed\`, \`blocked\`, and \`unknown\`
|
|
|
1417
1419
|
|---|---|
|
|
1418
1420
|
| \`TASK.md\` status metadata, \`## Status\`, and Status History | Command-owned for finish bookkeeping; worker-owned before finish. |
|
|
1419
1421
|
| \`docs/TASK_BOARD.md\` ID/title/status/capsule cells | Command-owned by \`task finish\`; Notes and extra cells are mixed/human-owned. |
|
|
1420
|
-
| \`EVIDENCE.md\` and \`evidence.jsonl\` | Evidence writer-owned; do not hand-edit \`evidence.jsonl\`. |
|
|
1422
|
+
| \`EVIDENCE.md\` and \`evidence.jsonl\` | Evidence writer-owned; do not hand-edit \`evidence.jsonl\`. Treat \`evidence.jsonl\` as canonical and \`EVIDENCE.md\` as a non-canonical human summary; evidence rebuild is not implemented in this scaffold and any future execute mode must be dry-run-first and before-hash guarded. |
|
|
1421
1423
|
| \`HANDOFF.md\` managed current-state table | Managed/mixed; persist \`TaskStatus\` only. \`CloseState\` is derived by status/audit/proof/state read models and should not be written into close-source handoff tables. |
|
|
1422
1424
|
| Shared state docs | Mixed/human-owned; update before close when they are close-source relevant. |
|
|
1423
1425
|
| \`.hadara/docs-registry.json\` and \`docs/DOC_REGISTRY.md\` | Docs registry-owned; registry mutations should stay dry-run-first or explicitly scoped. |
|
|
@@ -1439,7 +1441,7 @@ Serialize same-file writes, evidence append, Task Capsule doc writes, Task Board
|
|
|
1439
1441
|
| \`task next\` | Read-only | Recommends work; does not create tasks. |
|
|
1440
1442
|
| \`task status\` | Read-only | \`ok\` means report generation succeeded; readiness is in \`state.ready\`, \`summary.blockers\`, and \`issues\`. |
|
|
1441
1443
|
| \`task create\` | Write | Creates a Draft Task Capsule and Task Board row. It does not imply the task is ready or done. |
|
|
1442
|
-
| \`evidence add-command\` | Write | Appends operator-supplied command-log evidence. It does not execute shell commands or capture stdout/stderr; optional \`--idempotency-key\` prevents duplicate same-key records. |
|
|
1444
|
+
| \`evidence add-command\` | Write | Appends operator-supplied command-log evidence. It does not execute shell commands or capture stdout/stderr; optional \`--category\`/\`--outcome\`/\`--resolves\`/\`--supersedes\` enrich v2 metadata, result/outcome mismatches are rejected, and optional \`--idempotency-key\` prevents duplicate same-key records. |
|
|
1443
1445
|
| \`task finish\` | Dry-run by default; writes only with \`--execute\` | Updates only \`TASK.md\` status bookkeeping and the matching \`docs/TASK_BOARD.md\` row. |
|
|
1444
1446
|
| \`task ready\` | Read-only | Checks whether the task can satisfy the requested readiness level after finish. |
|
|
1445
1447
|
| \`task complete\` | Read-only | Summarizes the current completion stage and next command; it does not execute lifecycle writes. |
|
|
@@ -1453,7 +1455,9 @@ Serialize same-file writes, evidence append, Task Capsule doc writes, Task Board
|
|
|
1453
1455
|
- \`task ready\` checks readiness; it does not write evidence or status.
|
|
1454
1456
|
- \`harness validate\` is a direct diagnostic for Task Capsule structure and done-level gates; it is not a replacement for close evidence.
|
|
1455
1457
|
- \`task complete\` is a read-only workflow compressor. It may report the next lifecycle command, but it must not execute finish, ready, close, or audit commands.
|
|
1456
|
-
- \`evidence
|
|
1458
|
+
- \`evidence list\` is the supported evidence id discovery surface. Text output shows \`[id] time | category/outcome | visibility | summary\`; JSON records expose \`id\`, \`idSource\`, \`idStability\`, \`persistedSchemaVersion\`, \`category\`, \`outcome\`, and \`tags\`. Use durable persisted \`ev:\` ids for long-lived \`--resolves\` and \`--supersedes\` references. Legacy compatibility ids are inspection-only and are not the preferred durable reference.
|
|
1459
|
+
- \`evidence add-command\` records an operator-supplied command result; it does not run the command. \`--category\` and \`--outcome\` set persisted v2 metadata explicitly, while \`--result\` remains the legacy-compatible command result. When both are supplied, \`--result\` must match \`--outcome\` for \`passed\`, \`failed\`, \`blocked\`, and \`unknown\`; \`recorded\` and \`not-applicable\` require \`--result unknown\` or no explicit \`--result\`. \`--resolves\` and \`--supersedes\` append exact v2 resolution tags from passed or recorded follow-up evidence. \`--idempotency-key\` is optional; when supplied, same-key repeats return the existing record without appending duplicate Markdown or JSONL rows.
|
|
1460
|
+
- Evidence v2 deferred scope remains explicit: rebuild preview/execute, \`check-id\`, \`subject\`, and a new add-command report schema id are future candidates. Do not infer those commands or schema changes from the current \`evidence list\` and \`evidence add-command\` ergonomics.
|
|
1457
1461
|
- \`task finish\` may update only the Task Capsule \`TASK.md\` status and the matching \`docs/TASK_BOARD.md\` row's command-owned cells: \`ID\`, \`Title\`, \`Status\`, and \`Capsule\`. It preserves human/mixed-owned \`Notes\` and any extra cells.
|
|
1458
1462
|
- \`task close\` may append only close evidence. It must not update status docs, Task Board rows, handoff, Project State, roadmap docs, or arbitrary evidence.
|
|
1459
1463
|
- After \`task close --execute --json\`, close-source document edits intentionally invalidate the previous close proof. Make those edits before close, or rerun ready/close/audit if the edit is unavoidable.
|
|
@@ -3,10 +3,11 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
3
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.EvidenceAppendLockError = exports.EvidenceArtifactPolicyError = void 0;
|
|
6
|
+
exports.EvidenceTaskDirectoryError = exports.EvidenceResultOutcomeMismatchError = exports.EvidenceAppendLockError = exports.EvidenceArtifactPolicyError = void 0;
|
|
7
7
|
exports.persistedEvidenceKind = persistedEvidenceKind;
|
|
8
8
|
exports.persistedEvidenceResult = persistedEvidenceResult;
|
|
9
9
|
exports.persistedEvidencePath = persistedEvidencePath;
|
|
10
|
+
exports.validateEvidenceResultOutcomeCompatibility = validateEvidenceResultOutcomeCompatibility;
|
|
10
11
|
exports.appendEvidence = appendEvidence;
|
|
11
12
|
exports.appendEvidenceWithResult = appendEvidenceWithResult;
|
|
12
13
|
exports.appendEvidenceTextArtifact = appendEvidenceTextArtifact;
|
|
@@ -48,13 +49,53 @@ class EvidenceAppendLockError extends Error {
|
|
|
48
49
|
}
|
|
49
50
|
}
|
|
50
51
|
exports.EvidenceAppendLockError = EvidenceAppendLockError;
|
|
52
|
+
class EvidenceResultOutcomeMismatchError extends Error {
|
|
53
|
+
issue;
|
|
54
|
+
code = 'EVIDENCE_RESULT_OUTCOME_MISMATCH';
|
|
55
|
+
constructor(issue) {
|
|
56
|
+
super(issue.message);
|
|
57
|
+
this.issue = issue;
|
|
58
|
+
this.name = 'EvidenceResultOutcomeMismatchError';
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
exports.EvidenceResultOutcomeMismatchError = EvidenceResultOutcomeMismatchError;
|
|
62
|
+
class EvidenceTaskDirectoryError extends Error {
|
|
63
|
+
code;
|
|
64
|
+
constructor(code, message) {
|
|
65
|
+
super(message);
|
|
66
|
+
this.code = code;
|
|
67
|
+
this.name = 'EvidenceTaskDirectoryError';
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
exports.EvidenceTaskDirectoryError = EvidenceTaskDirectoryError;
|
|
71
|
+
function validateEvidenceResultOutcomeCompatibility(input) {
|
|
72
|
+
if (!input.outcome)
|
|
73
|
+
return undefined;
|
|
74
|
+
if (input.outcome === 'passed' || input.outcome === 'failed' || input.outcome === 'blocked' || input.outcome === 'unknown') {
|
|
75
|
+
if (input.result === input.outcome)
|
|
76
|
+
return undefined;
|
|
77
|
+
return {
|
|
78
|
+
severity: 'error',
|
|
79
|
+
code: 'EVIDENCE_RESULT_OUTCOME_MISMATCH',
|
|
80
|
+
message: `Evidence result ${input.result} conflicts with outcome ${input.outcome}; matching evidence outcomes must use the same legacy result.`
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
if ((input.outcome === 'recorded' || input.outcome === 'not-applicable') && input.result !== 'unknown') {
|
|
84
|
+
return {
|
|
85
|
+
severity: 'error',
|
|
86
|
+
code: 'EVIDENCE_RESULT_OUTCOME_MISMATCH',
|
|
87
|
+
message: `Evidence outcome ${input.outcome} is record-only/non-applicable evidence and requires legacy result unknown.`
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
return undefined;
|
|
91
|
+
}
|
|
51
92
|
function appendEvidence(projectRoot, record) {
|
|
52
93
|
return appendEvidenceWithResult(projectRoot, record).markdownPath;
|
|
53
94
|
}
|
|
54
95
|
function appendEvidenceWithResult(projectRoot, record) {
|
|
55
96
|
const taskDir = findTaskDir(projectRoot, record.taskId);
|
|
56
97
|
if (!taskDir) {
|
|
57
|
-
throw new
|
|
98
|
+
throw new EvidenceTaskDirectoryError('TASK_NOT_FOUND', `Task Capsule not found: ${record.taskId}`);
|
|
58
99
|
}
|
|
59
100
|
const time = new Date().toISOString();
|
|
60
101
|
const visibility = record.visibility ?? 'public';
|
|
@@ -70,7 +111,7 @@ function appendEvidenceWithResult(projectRoot, record) {
|
|
|
70
111
|
function appendEvidenceTextArtifact(projectRoot, record, artifact, options = {}) {
|
|
71
112
|
const taskDir = findTaskDir(projectRoot, record.taskId);
|
|
72
113
|
if (!taskDir) {
|
|
73
|
-
throw new
|
|
114
|
+
throw new EvidenceTaskDirectoryError('TASK_NOT_FOUND', `Task Capsule not found: ${record.taskId}`);
|
|
74
115
|
}
|
|
75
116
|
const time = new Date().toISOString();
|
|
76
117
|
const visibility = record.visibility ?? 'public';
|
|
@@ -182,6 +223,13 @@ function sleepSync(ms) {
|
|
|
182
223
|
Atomics.wait(signal, 0, 0, ms);
|
|
183
224
|
}
|
|
184
225
|
function appendEvidenceRecord(input) {
|
|
226
|
+
const resultOutcomeIssue = validateEvidenceResultOutcomeCompatibility({
|
|
227
|
+
result: input.record.result,
|
|
228
|
+
outcome: input.record.outcome
|
|
229
|
+
});
|
|
230
|
+
if (resultOutcomeIssue) {
|
|
231
|
+
throw new EvidenceResultOutcomeMismatchError(resultOutcomeIssue);
|
|
232
|
+
}
|
|
185
233
|
const summary = (0, redaction_1.redactSecrets)(input.record.summary.replace(/\|/g, '/'));
|
|
186
234
|
const markdownPath = node_path_1.default.join(input.taskDir, 'EVIDENCE.md');
|
|
187
235
|
return withEvidenceAppendLock(input.projectRoot, input.record.taskId, () => {
|
|
@@ -212,6 +260,8 @@ function appendEvidenceRecord(input) {
|
|
|
212
260
|
kind: input.record.kind,
|
|
213
261
|
summary,
|
|
214
262
|
result: input.record.result,
|
|
263
|
+
category: input.record.category,
|
|
264
|
+
outcome: input.record.outcome,
|
|
215
265
|
visibility: input.visibility,
|
|
216
266
|
attachedPath,
|
|
217
267
|
tags: input.record.tags,
|
|
@@ -250,8 +300,8 @@ function createEvidenceV2Record(input) {
|
|
|
250
300
|
schemaVersion: 'hadara.evidence.v2',
|
|
251
301
|
time: input.time,
|
|
252
302
|
taskId: input.taskId,
|
|
253
|
-
category: deriveEvidenceCategory(input.kind, input.summary),
|
|
254
|
-
outcome: normalizeEvidenceOutcome(input.result),
|
|
303
|
+
category: input.category ?? deriveEvidenceCategory(input.kind, input.summary),
|
|
304
|
+
outcome: input.outcome ?? normalizeEvidenceOutcome(input.result),
|
|
255
305
|
visibility: input.visibility,
|
|
256
306
|
summary: input.summary,
|
|
257
307
|
artifacts: input.visibility === 'public' && input.attachedPath
|
|
@@ -375,8 +425,13 @@ function findTaskDir(projectRoot, taskId) {
|
|
|
375
425
|
const tasksDir = node_path_1.default.join(projectRoot, 'tasks');
|
|
376
426
|
if (!node_fs_1.default.existsSync(tasksDir))
|
|
377
427
|
return null;
|
|
378
|
-
const
|
|
379
|
-
|
|
428
|
+
const entries = node_fs_1.default
|
|
429
|
+
.readdirSync(tasksDir, { withFileTypes: true })
|
|
430
|
+
.filter((entry) => entry.isDirectory() && entry.name.startsWith(`${taskId}-`) && node_fs_1.default.existsSync(node_path_1.default.join(tasksDir, entry.name, 'TASK.md')));
|
|
431
|
+
if (entries.length > 1) {
|
|
432
|
+
throw new EvidenceTaskDirectoryError('TASK_CAPSULE_AMBIGUOUS', `Multiple Task Capsules found for ${taskId}; remove or repair duplicate TASK.md-bearing directories before recording evidence.`);
|
|
433
|
+
}
|
|
434
|
+
return entries[0] ? node_path_1.default.join(tasksDir, entries[0].name) : null;
|
|
380
435
|
}
|
|
381
436
|
function createSessionEvidenceDirs(dataRoot, sessionId) {
|
|
382
437
|
const evidenceDir = node_path_1.default.join(dataRoot, 'sessions', sessionId, 'evidence');
|
|
@@ -82,10 +82,10 @@ function findUnresolvedFailedEvidence(records, taskDocs = {}) {
|
|
|
82
82
|
if (record.outcome !== 'failed')
|
|
83
83
|
return false;
|
|
84
84
|
const laterRecords = records.slice(index + 1);
|
|
85
|
-
if (laterRecords.some((candidate) => candidate.outcome === 'passed' && candidate.category === record.category))
|
|
86
|
-
return false;
|
|
87
85
|
if (laterRecords.some((candidate) => hasExactResolutionMarker(candidate, record.id)))
|
|
88
86
|
return false;
|
|
87
|
+
if (usesLegacySameCategoryFallback(record, laterRecords))
|
|
88
|
+
return false;
|
|
89
89
|
if (hasResidualRiskDocumentation(record, taskDocs))
|
|
90
90
|
return false;
|
|
91
91
|
return true;
|
|
@@ -137,7 +137,7 @@ function analyzeTaskEvidenceSemantics(input) {
|
|
|
137
137
|
message: 'Task has unresolved failed evidence while marked Done.',
|
|
138
138
|
evidenceId: record.id,
|
|
139
139
|
path: `${input.taskDir}/evidence.jsonl`,
|
|
140
|
-
expected: `
|
|
140
|
+
expected: `supersedes:${record.id}, resolves:${record.id}, legacy same-category passed evidence, or explicit residual-risk documentation`,
|
|
141
141
|
actual: 'failed evidence has no resolution signal'
|
|
142
142
|
});
|
|
143
143
|
}
|
|
@@ -191,8 +191,17 @@ function isRecordOnlyCategory(category) {
|
|
|
191
191
|
return exports.RECORD_ONLY_EVIDENCE_CATEGORIES.includes(category);
|
|
192
192
|
}
|
|
193
193
|
function hasExactResolutionMarker(record, evidenceId) {
|
|
194
|
+
if (record.outcome !== 'passed' && record.outcome !== 'recorded')
|
|
195
|
+
return false;
|
|
194
196
|
return record.tags.includes(`supersedes:${evidenceId}`) || record.tags.includes(`resolves:${evidenceId}`);
|
|
195
197
|
}
|
|
198
|
+
function usesLegacySameCategoryFallback(record, laterRecords) {
|
|
199
|
+
if (record.persistedSchemaVersion !== 'hadara.evidence.v1')
|
|
200
|
+
return false;
|
|
201
|
+
return laterRecords.some((candidate) => candidate.persistedSchemaVersion === 'hadara.evidence.v1' &&
|
|
202
|
+
candidate.outcome === 'passed' &&
|
|
203
|
+
candidate.category === record.category);
|
|
204
|
+
}
|
|
196
205
|
function hasResidualRiskDocumentation(record, taskDocs) {
|
|
197
206
|
const content = joinTaskDocs(taskDocs);
|
|
198
207
|
if (!content)
|
|
@@ -25,25 +25,44 @@
|
|
|
25
25
|
"evidenceV1": {
|
|
26
26
|
"type": "object",
|
|
27
27
|
"additionalProperties": true,
|
|
28
|
-
"required": ["schemaVersion", "time", "taskId", "kind", "summary", "result", "visibility"],
|
|
28
|
+
"required": ["schemaVersion", "id", "sourceLine", "idSource", "idStability", "persistedSchemaVersion", "time", "taskId", "kind", "summary", "result", "visibility", "category", "outcome", "tags", "legacy"],
|
|
29
29
|
"properties": {
|
|
30
30
|
"schemaVersion": { "const": "hadara.evidence.v1" },
|
|
31
|
+
"id": { "type": "string", "pattern": "^legacy:T-[0-9]{4}:[0-9]+:[a-f0-9]{12}$" },
|
|
32
|
+
"sourceLine": { "type": "integer", "minimum": 1 },
|
|
33
|
+
"idSource": { "const": "line-fallback" },
|
|
34
|
+
"idStability": { "const": "unstable-on-reorder" },
|
|
35
|
+
"persistedSchemaVersion": { "const": "hadara.evidence.v1" },
|
|
31
36
|
"time": { "type": "string", "minLength": 1 },
|
|
32
37
|
"taskId": { "type": "string", "pattern": "^T-[0-9]{4}$" },
|
|
33
38
|
"kind": { "type": "string", "enum": ["test-log", "command-log", "diff-summary", "screenshot", "note"] },
|
|
34
39
|
"summary": { "type": "string", "minLength": 1 },
|
|
35
40
|
"result": { "type": "string", "enum": ["passed", "failed", "blocked", "unknown"] },
|
|
36
|
-
"visibility": { "type": "string", "enum": ["public", "private"] }
|
|
41
|
+
"visibility": { "type": "string", "enum": ["public", "private"] },
|
|
42
|
+
"category": { "type": "string", "enum": ["validation", "implementation", "release", "security", "policy", "operation", "decision", "handoff", "audit", "note", "observation"] },
|
|
43
|
+
"outcome": { "type": "string", "enum": ["passed", "failed", "blocked", "unknown", "recorded", "not-applicable"] },
|
|
44
|
+
"tags": { "type": "array", "items": { "type": "string" } },
|
|
45
|
+
"legacy": {
|
|
46
|
+
"type": "object",
|
|
47
|
+
"additionalProperties": true,
|
|
48
|
+
"required": ["kind", "result"],
|
|
49
|
+
"properties": {
|
|
50
|
+
"kind": { "type": "string", "enum": ["test-log", "command-log", "diff-summary", "screenshot", "note"] },
|
|
51
|
+
"result": { "type": "string", "enum": ["passed", "failed", "blocked", "unknown"] },
|
|
52
|
+
"evidencePath": { "type": "string", "minLength": 1 }
|
|
53
|
+
}
|
|
54
|
+
}
|
|
37
55
|
}
|
|
38
56
|
},
|
|
39
57
|
"evidenceV2": {
|
|
40
58
|
"type": "object",
|
|
41
59
|
"additionalProperties": true,
|
|
42
|
-
"required": ["schemaVersion", "id", "fingerprint", "idSource", "idStability", "time", "taskId", "category", "outcome", "visibility", "summary", "artifacts", "tags", "legacy"],
|
|
60
|
+
"required": ["schemaVersion", "id", "sourceLine", "persistedSchemaVersion", "fingerprint", "idSource", "idStability", "time", "taskId", "category", "outcome", "visibility", "summary", "artifacts", "tags", "legacy"],
|
|
43
61
|
"properties": {
|
|
44
62
|
"schemaVersion": { "const": "hadara.evidence.v2" },
|
|
45
63
|
"id": { "type": "string", "minLength": 1 },
|
|
46
64
|
"sourceLine": { "type": "integer", "minimum": 1 },
|
|
65
|
+
"persistedSchemaVersion": { "const": "hadara.evidence.v2" },
|
|
47
66
|
"fingerprint": { "type": "string", "pattern": "^sha256:[a-f0-9]{64}$" },
|
|
48
67
|
"idSource": { "const": "persisted" },
|
|
49
68
|
"idStability": { "const": "durable" },
|
|
@@ -475,8 +475,8 @@ exports.HADARA_COMMAND_REGISTRY = [
|
|
|
475
475
|
},
|
|
476
476
|
{
|
|
477
477
|
id: 'evidence.add-command',
|
|
478
|
-
command: 'hadara evidence add-command --task <task-id> --summary <text> [--result <result>] [--idempotency-key <key>] [--json]',
|
|
479
|
-
summary: 'Append command-log evidence to a Task Capsule.',
|
|
478
|
+
command: 'hadara evidence add-command --task <task-id> --summary <text> [--result <result>] [--outcome <outcome>] [--category <category>] [--resolves <id>] [--supersedes <id>] [--idempotency-key <key>] [--json]',
|
|
479
|
+
summary: 'Append command-log evidence to a Task Capsule, rejecting incompatible result/outcome metadata.',
|
|
480
480
|
canonical: true,
|
|
481
481
|
appearsInDefaultHelp: true,
|
|
482
482
|
family: 'capsule-lifecycle',
|
|
@@ -490,14 +490,17 @@ exports.HADARA_COMMAND_REGISTRY = [
|
|
|
490
490
|
status: 'stable',
|
|
491
491
|
schemaVersion: 'hadara.evidence.collect.v1',
|
|
492
492
|
docs: ['docs/IMPLEMENTATION_SOP.md'],
|
|
493
|
-
examples: [
|
|
493
|
+
examples: [
|
|
494
|
+
example('Record command evidence', 'hadara evidence add-command --task T-0001 --summary "npm test passed" --result passed --category validation --json', 'After meaningful validation.')
|
|
495
|
+
],
|
|
494
496
|
related: ['evidence.list', 'evidence.lint', 'task.ready'],
|
|
497
|
+
notes: 'The collect response remains `hadara.evidence.collect.v1` with additive v2 metadata. A new add-command report schema id, check-id, and subject fields are deferred candidate scope.',
|
|
495
498
|
conflictsWith: []
|
|
496
499
|
},
|
|
497
500
|
{
|
|
498
501
|
id: 'evidence.list',
|
|
499
502
|
command: 'hadara evidence list --task <task-id> [--limit <n>] [--include-private] [--json]',
|
|
500
|
-
summary: 'List
|
|
503
|
+
summary: 'List Task Capsule evidence ids, category/outcome metadata, and sanitized evidence summaries.',
|
|
501
504
|
canonical: true,
|
|
502
505
|
appearsInDefaultHelp: false,
|
|
503
506
|
family: 'capsule-lifecycle',
|
|
@@ -511,7 +514,7 @@ exports.HADARA_COMMAND_REGISTRY = [
|
|
|
511
514
|
status: 'stable',
|
|
512
515
|
schemaVersion: 'hadara.evidence.list.v1',
|
|
513
516
|
docs: ['docs/IMPLEMENTATION_SOP.md'],
|
|
514
|
-
examples: [example('List evidence', 'hadara evidence list --task T-0001
|
|
517
|
+
examples: [example('List evidence ids', 'hadara evidence list --task T-0001', 'Before copying a durable ev: id into --resolves or --supersedes.')],
|
|
515
518
|
related: ['evidence.add-command', 'evidence.lint'],
|
|
516
519
|
conflictsWith: []
|
|
517
520
|
},
|
|
@@ -552,9 +555,10 @@ exports.HADARA_COMMAND_REGISTRY = [
|
|
|
552
555
|
actor: 'operator',
|
|
553
556
|
status: 'stable',
|
|
554
557
|
schemaVersion: 'hadara.evidence.migration_preview.v1',
|
|
555
|
-
docs: ['docs/EVIDENCE_SEMANTIC_CONTRACT.md'],
|
|
558
|
+
docs: ['docs/EVIDENCE_SEMANTIC_CONTRACT.md', 'docs/CLI_JSON_CONTRACT.md'],
|
|
556
559
|
examples: [example('Preview evidence migration', 'hadara evidence migrate --task T-0001 --to v2 --json', 'When older evidence files need migration.')],
|
|
557
560
|
related: ['evidence.lint', 'protocol.remediate'],
|
|
561
|
+
notes: 'This is an operator-selected JSONL migration surface, not evidence rebuild. 0.3.2 does not register or implement `hadara evidence rebuild`; `EVIDENCE.md` remains a non-canonical human summary.',
|
|
558
562
|
conflictsWith: []
|
|
559
563
|
},
|
|
560
564
|
{
|
|
@@ -8,6 +8,7 @@ exports.parseEvidenceIndexFile = parseEvidenceIndexFile;
|
|
|
8
8
|
const node_fs_1 = __importDefault(require("node:fs"));
|
|
9
9
|
const node_path_1 = __importDefault(require("node:path"));
|
|
10
10
|
const redaction_1 = require("../core/redaction");
|
|
11
|
+
const normalizer_1 = require("../evidence/normalizer");
|
|
11
12
|
const task_capsule_1 = require("../task/task-capsule");
|
|
12
13
|
const DEFAULT_LIMIT = 50;
|
|
13
14
|
function createEvidenceListReport(projectRoot, input) {
|
|
@@ -73,7 +74,7 @@ function parseEvidenceIndexFile(indexPath, taskId) {
|
|
|
73
74
|
});
|
|
74
75
|
return;
|
|
75
76
|
}
|
|
76
|
-
records.push(normalizeEvidenceIndexRecord(record));
|
|
77
|
+
records.push(normalizeEvidenceIndexRecord(record, index + 1));
|
|
77
78
|
return;
|
|
78
79
|
}
|
|
79
80
|
issues.push({
|
|
@@ -132,9 +133,10 @@ function isEvidenceV2IndexRecord(value) {
|
|
|
132
133
|
isEvidenceResult(record.legacy.result) &&
|
|
133
134
|
(record.legacy.evidencePath === undefined || typeof record.legacy.evidencePath === 'string'));
|
|
134
135
|
}
|
|
135
|
-
function normalizeEvidenceIndexRecord(record) {
|
|
136
|
+
function normalizeEvidenceIndexRecord(record, sourceLine) {
|
|
136
137
|
if (record.schemaVersion === 'hadara.evidence.v2')
|
|
137
|
-
return normalizeEvidenceV2IndexRecord(record);
|
|
138
|
+
return normalizeEvidenceV2IndexRecord(record, sourceLine);
|
|
139
|
+
const normalizedRecord = (0, normalizer_1.normalizeEvidenceRecord)(record, { lineNumber: sourceLine });
|
|
138
140
|
const normalized = {
|
|
139
141
|
schemaVersion: 'hadara.evidence.v1',
|
|
140
142
|
time: record.time,
|
|
@@ -147,13 +149,29 @@ function normalizeEvidenceIndexRecord(record) {
|
|
|
147
149
|
if (record.visibility === 'public' && record.evidencePath) {
|
|
148
150
|
normalized.evidencePath = record.evidencePath;
|
|
149
151
|
}
|
|
150
|
-
return
|
|
152
|
+
return {
|
|
153
|
+
...normalized,
|
|
154
|
+
id: normalizedRecord.id,
|
|
155
|
+
sourceLine,
|
|
156
|
+
idSource: 'line-fallback',
|
|
157
|
+
idStability: 'unstable-on-reorder',
|
|
158
|
+
persistedSchemaVersion: 'hadara.evidence.v1',
|
|
159
|
+
category: normalizedRecord.category,
|
|
160
|
+
outcome: normalizedRecord.outcome,
|
|
161
|
+
tags: normalizedRecord.tags,
|
|
162
|
+
legacy: {
|
|
163
|
+
kind: normalized.kind,
|
|
164
|
+
result: normalized.result,
|
|
165
|
+
...(normalized.evidencePath ? { evidencePath: normalized.evidencePath } : {})
|
|
166
|
+
}
|
|
167
|
+
};
|
|
151
168
|
}
|
|
152
|
-
function normalizeEvidenceV2IndexRecord(record) {
|
|
169
|
+
function normalizeEvidenceV2IndexRecord(record, sourceLine) {
|
|
153
170
|
return {
|
|
154
171
|
schemaVersion: 'hadara.evidence.v2',
|
|
155
172
|
id: record.id,
|
|
156
|
-
|
|
173
|
+
sourceLine: record.sourceLine ?? sourceLine,
|
|
174
|
+
persistedSchemaVersion: 'hadara.evidence.v2',
|
|
157
175
|
fingerprint: record.fingerprint,
|
|
158
176
|
idSource: 'persisted',
|
|
159
177
|
idStability: 'durable',
|
package/dist/tui/read-model.js
CHANGED
|
@@ -349,7 +349,7 @@ function createSelectedTaskReadModel(projectRoot, summary, options = {}) {
|
|
|
349
349
|
}
|
|
350
350
|
function createFastSelectedTaskReadModel(projectRoot, summary, options) {
|
|
351
351
|
const detail = createTaskDocumentReadReportFromSummary(projectRoot, summary, { includePrivate: false });
|
|
352
|
-
const evidenceIndex = detail.evidenceIndex ?? [];
|
|
352
|
+
const evidenceIndex = (detail.evidenceIndex ?? []);
|
|
353
353
|
const evidence = limitEvidenceListReport({
|
|
354
354
|
schemaVersion: 'hadara.evidence.list.v1',
|
|
355
355
|
command: 'evidence.list',
|