hadara 0.4.0-rc.0 → 0.4.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/README.md +19 -19
- package/dist/cli/doctor.js +74 -3
- package/dist/cli/evidence-json.js +3 -0
- package/dist/cli/evidence.js +1 -0
- package/dist/cli/release-artifact.js +1 -1
- package/dist/cli/task.js +65 -11
- package/dist/cli/validation.js +14 -4
- package/dist/core/timing.js +12 -0
- package/dist/dev/docker-check.js +3 -2
- package/dist/evidence/evidence.js +3 -2
- package/dist/handoff/handoff-suggestion.js +1 -0
- package/dist/handoff/handoff.js +1 -0
- package/dist/harness/validate.js +140 -36
- package/dist/services/capability-registry.js +6 -5
- package/dist/services/ci-gate.js +1 -0
- package/dist/services/clean-checkout-smoke.js +4 -2
- package/dist/services/dashboard-bootstrap.js +16 -1
- package/dist/services/dashboard-task-detail.js +1 -0
- package/dist/services/package-recycle.js +5 -2
- package/dist/services/package-smoke.js +7 -2
- package/dist/services/proof-status.js +2 -0
- package/dist/services/protocol-consistency.js +2 -1
- package/dist/services/protocol-migration.js +1 -0
- package/dist/services/release-artifact.js +3 -2
- package/dist/services/release-closeout.js +1 -0
- package/dist/services/release-diagnostics.js +5 -4
- package/dist/services/release-dry-run.js +3 -2
- package/dist/services/task-read-model.js +4 -0
- package/dist/services/task-workbench.js +27 -11
- package/dist/services/validation-run.js +11 -3
- package/dist/services/write-preflight.js +2 -13
- package/dist/task/acceptance.js +27 -5
- package/dist/task/authoring-guidance.js +6 -4
- package/dist/task/task-capsule.js +20 -9
- package/dist/task/task-create.js +1 -0
- package/dist/task/task-templates.js +1 -1
- package/dist/task/task-upgrade-scaffold.js +8 -6
- package/dist/tui/read-model.js +2 -0
- 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.
|
|
9
|
-
<img alt="
|
|
8
|
+
<img alt="Stable npm release" src="https://img.shields.io/badge/npm-0.4.0-blue">
|
|
9
|
+
<img alt="Next npm release" src="https://img.shields.io/badge/next-0.4.0--rc.0-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,30 +24,30 @@ 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.
|
|
27
|
+
hadara@0.4.0
|
|
28
28
|
```
|
|
29
29
|
|
|
30
|
-
|
|
30
|
+
Previous npm release candidate:
|
|
31
31
|
|
|
32
32
|
```text
|
|
33
33
|
hadara@0.4.0-rc.0
|
|
34
34
|
```
|
|
35
35
|
|
|
36
|
-
Previous
|
|
36
|
+
Previous agent UX release candidate:
|
|
37
37
|
|
|
38
38
|
```text
|
|
39
39
|
hadara@0.3.4-rc.0
|
|
40
40
|
```
|
|
41
41
|
|
|
42
|
-
The 0.4.0 source line is a breaking productization release
|
|
42
|
+
The 0.4.0 source line is a breaking productization release after stable 0.3.3 and the published 0.3.4-rc.0 agent UX line. It keeps the context-routing and finalize-first lifecycle baseline, then simplifies new projects around a productized 0.4 scaffold, registry-backed docs read maps, a four-file Task Capsule, generated evidence projection, a normalized close-source contract, and fail-closed legacy mutation boundaries.
|
|
43
43
|
|
|
44
|
-
Phase labels are internal implementation phases, not npm release-candidate labels. The stable `0.
|
|
44
|
+
Phase labels are internal implementation phases, not npm release-candidate labels. The stable `0.4.0` package is the default install target through the `latest` dist-tag after the approval-gated publish completes; `0.4.0-rc.0` remains the previous release candidate on npm under the `next` dist-tag.
|
|
45
45
|
|
|
46
46
|
| Surface | Status |
|
|
47
47
|
|---|---|
|
|
48
|
-
| Current stable | [`hadara@0.
|
|
49
|
-
|
|
|
50
|
-
| Previous RC | [`hadara@0.3.4-rc.0`](docs/RELEASE_NOTES.md#034-rc0) |
|
|
48
|
+
| Current stable | [`hadara@0.4.0`](docs/RELEASE_NOTES.md#040) |
|
|
49
|
+
| Previous RC | [`hadara@0.4.0-rc.0`](docs/RELEASE_NOTES.md#040-rc0) on `next` |
|
|
50
|
+
| Previous agent UX RC | [`hadara@0.3.4-rc.0`](docs/RELEASE_NOTES.md#034-rc0) |
|
|
51
51
|
| Previous stable | [`hadara@0.3.2`](docs/RELEASE_NOTES.md#032) |
|
|
52
52
|
| Historical RCs | See [Release Notes](docs/RELEASE_NOTES.md). |
|
|
53
53
|
| GitHub Release | Secondary target, approval-gated. |
|
|
@@ -55,7 +55,7 @@ Phase labels are internal implementation phases, not npm release-candidate label
|
|
|
55
55
|
| PyPI/Python package | `hadara==0.2.0rc1` published preview bridge. |
|
|
56
56
|
| Installer scripts / USB launchers | Deferred. |
|
|
57
57
|
|
|
58
|
-
No release command should publish, create a GitHub Release, build Docker images, upload artifacts, or load token values unless an operator explicitly approves the mutation path for the active release capsule. Stable `0.
|
|
58
|
+
No release command should publish, create a GitHub Release, build Docker images, upload artifacts, or load token values unless an operator explicitly approves the mutation path for the active release capsule. Stable `0.4.0` is the default npm package line after the T-0490 approval-gated publish completes; `0.4.0-rc.0` remains available for explicit RC evaluation through `hadara@next` or the exact version. The `v0.4.0-rc.0` GitHub Release exists as a draft prerelease; Docker/PyPI publish, installer execution, and MCP release/package execution remain separate explicit mutations.
|
|
59
59
|
|
|
60
60
|
## Install
|
|
61
61
|
|
|
@@ -64,7 +64,7 @@ Requires Node.js 22.
|
|
|
64
64
|
Install the stable release:
|
|
65
65
|
|
|
66
66
|
```bash
|
|
67
|
-
npm install -g hadara@0.
|
|
67
|
+
npm install -g hadara@0.4.0
|
|
68
68
|
hadara help
|
|
69
69
|
hadara doctor --json
|
|
70
70
|
```
|
|
@@ -72,11 +72,11 @@ hadara doctor --json
|
|
|
72
72
|
Run without a global install:
|
|
73
73
|
|
|
74
74
|
```bash
|
|
75
|
-
npx hadara@0.
|
|
76
|
-
npx hadara@0.
|
|
75
|
+
npx hadara@0.4.0 help
|
|
76
|
+
npx hadara@0.4.0 doctor --json
|
|
77
77
|
```
|
|
78
78
|
|
|
79
|
-
|
|
79
|
+
Evaluate the previous `0.4.0-rc.0` explicitly:
|
|
80
80
|
|
|
81
81
|
```bash
|
|
82
82
|
npm install -g hadara@0.4.0-rc.0
|
|
@@ -87,11 +87,11 @@ For release or recycle evidence, prefer an isolated prefix install when PATH, gl
|
|
|
87
87
|
|
|
88
88
|
```bash
|
|
89
89
|
tmp="$(mktemp -d)"
|
|
90
|
-
npm --prefix "$tmp" install hadara@0.
|
|
90
|
+
npm --prefix "$tmp" install hadara@0.4.0
|
|
91
91
|
"$tmp/node_modules/.bin/hadara" version --json
|
|
92
92
|
```
|
|
93
93
|
|
|
94
|
-
`npx hadara@0.
|
|
94
|
+
`npx hadara@0.4.0 ...` 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.
|
|
95
95
|
|
|
96
96
|
## What HADARA Gives You
|
|
97
97
|
|
|
@@ -268,7 +268,7 @@ Dashboard, TUI, Hermes, MCP, installer, package, release, and run commands stay
|
|
|
268
268
|
|
|
269
269
|
## Safety Boundaries
|
|
270
270
|
|
|
271
|
-
HADARA 0.4.0
|
|
271
|
+
HADARA 0.4.0 is not:
|
|
272
272
|
|
|
273
273
|
- a full agent runtime;
|
|
274
274
|
- Rack/enterprise behavior;
|
|
@@ -308,7 +308,7 @@ Portable/local state is not committed. Project docs, Task Capsules, and reduced
|
|
|
308
308
|
|
|
309
309
|
`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.
|
|
310
310
|
|
|
311
|
-
0.4.0
|
|
311
|
+
0.4.0 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.
|
|
312
312
|
|
|
313
313
|
## Development / Contributing
|
|
314
314
|
|
package/dist/cli/doctor.js
CHANGED
|
@@ -7,18 +7,33 @@ exports.createDoctorReport = createDoctorReport;
|
|
|
7
7
|
exports.formatDoctorReport = formatDoctorReport;
|
|
8
8
|
exports.handleDoctorCommand = handleDoctorCommand;
|
|
9
9
|
const node_fs_1 = __importDefault(require("node:fs"));
|
|
10
|
-
|
|
10
|
+
const node_path_1 = __importDefault(require("node:path"));
|
|
11
|
+
const package_json_1 = __importDefault(require("../../package.json"));
|
|
12
|
+
function createDoctorReport(paths, nodeVersion = process.version, options = {}) {
|
|
11
13
|
const checks = [
|
|
12
14
|
pathCheck('docs', paths.projectDocsDir),
|
|
13
15
|
pathCheck('tasks', paths.projectTasksDir),
|
|
14
16
|
pathCheck('project-context', `${paths.projectContextDir}/HADARA_CONTEXT.md`)
|
|
15
17
|
];
|
|
18
|
+
const executablePath = normalizeOptionalPath(options.cliEntry ?? process.argv[1] ?? null);
|
|
19
|
+
const resolvedExecutablePath = resolveExecutablePath(executablePath);
|
|
20
|
+
const packageRoot = findPackageRoot([resolvedExecutablePath, executablePath]);
|
|
16
21
|
return {
|
|
17
22
|
schemaVersion: 'hadara.doctor.v1',
|
|
18
23
|
command: 'doctor',
|
|
19
24
|
ok: checks.every((check) => check.status === 'ok'),
|
|
20
25
|
runtime: {
|
|
21
|
-
node: nodeVersion
|
|
26
|
+
node: nodeVersion,
|
|
27
|
+
nodePath: normalizePath(options.nodePath ?? process.execPath)
|
|
28
|
+
},
|
|
29
|
+
installation: {
|
|
30
|
+
executablePath,
|
|
31
|
+
resolvedExecutablePath,
|
|
32
|
+
packageRoot,
|
|
33
|
+
packageVersion: package_json_1.default.version,
|
|
34
|
+
registry: 'https://registry.npmjs.org',
|
|
35
|
+
installCommand: `npm install -g ${package_json_1.default.name}@${package_json_1.default.version}`,
|
|
36
|
+
latestInstallCommand: `npm install -g ${package_json_1.default.name}`
|
|
22
37
|
},
|
|
23
38
|
paths: {
|
|
24
39
|
portableRoot: paths.portableRoot,
|
|
@@ -35,11 +50,18 @@ function formatDoctorReport(report) {
|
|
|
35
50
|
`dataRoot: ${report.paths.dataRoot}`,
|
|
36
51
|
`projectRoot: ${report.paths.projectRoot}`,
|
|
37
52
|
`Node: ${report.runtime.node}`,
|
|
53
|
+
`Node path: ${report.runtime.nodePath}`,
|
|
54
|
+
'Install:',
|
|
55
|
+
` executable: ${report.installation.executablePath ?? 'unknown'}`,
|
|
56
|
+
` resolved: ${report.installation.resolvedExecutablePath ?? 'unknown'}`,
|
|
57
|
+
` package: ${report.installation.packageVersion}${report.installation.packageRoot ? ` at ${report.installation.packageRoot}` : ' (package root unknown)'}`,
|
|
58
|
+
` registry: ${report.installation.registry}`,
|
|
59
|
+
` install: ${report.installation.installCommand}`,
|
|
38
60
|
...report.checks.map((check) => `${formatCheckLabel(check.id)}${check.status}`)
|
|
39
61
|
].join('\n');
|
|
40
62
|
}
|
|
41
63
|
function handleDoctorCommand(input) {
|
|
42
|
-
const report = createDoctorReport(input.paths);
|
|
64
|
+
const report = createDoctorReport(input.paths, process.version, { cliEntry: process.argv[1] });
|
|
43
65
|
if (input.jsonOutput) {
|
|
44
66
|
console.log(JSON.stringify(report, null, 2));
|
|
45
67
|
}
|
|
@@ -57,6 +79,55 @@ function pathCheck(id, targetPath) {
|
|
|
57
79
|
path: targetPath
|
|
58
80
|
};
|
|
59
81
|
}
|
|
82
|
+
function normalizeOptionalPath(value) {
|
|
83
|
+
if (!value)
|
|
84
|
+
return null;
|
|
85
|
+
return normalizePath(value);
|
|
86
|
+
}
|
|
87
|
+
function normalizePath(value) {
|
|
88
|
+
return node_path_1.default.normalize(value);
|
|
89
|
+
}
|
|
90
|
+
function resolveExecutablePath(executablePath) {
|
|
91
|
+
if (!executablePath)
|
|
92
|
+
return null;
|
|
93
|
+
try {
|
|
94
|
+
return normalizePath(node_fs_1.default.realpathSync.native(executablePath));
|
|
95
|
+
}
|
|
96
|
+
catch {
|
|
97
|
+
return null;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
function findPackageRoot(candidates) {
|
|
101
|
+
for (const candidate of candidates) {
|
|
102
|
+
const found = candidate ? findPackageRootFromFile(candidate) : null;
|
|
103
|
+
if (found)
|
|
104
|
+
return found;
|
|
105
|
+
}
|
|
106
|
+
return null;
|
|
107
|
+
}
|
|
108
|
+
function findPackageRootFromFile(filePath) {
|
|
109
|
+
let current = node_fs_1.default.existsSync(filePath) && node_fs_1.default.statSync(filePath).isDirectory()
|
|
110
|
+
? filePath
|
|
111
|
+
: node_path_1.default.dirname(filePath);
|
|
112
|
+
while (true) {
|
|
113
|
+
const packagePath = node_path_1.default.join(current, 'package.json');
|
|
114
|
+
if (isHadaraPackageJson(packagePath))
|
|
115
|
+
return normalizePath(current);
|
|
116
|
+
const parent = node_path_1.default.dirname(current);
|
|
117
|
+
if (parent === current)
|
|
118
|
+
return null;
|
|
119
|
+
current = parent;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
function isHadaraPackageJson(packagePath) {
|
|
123
|
+
try {
|
|
124
|
+
const parsed = JSON.parse(node_fs_1.default.readFileSync(packagePath, 'utf8'));
|
|
125
|
+
return parsed.name === package_json_1.default.name;
|
|
126
|
+
}
|
|
127
|
+
catch {
|
|
128
|
+
return false;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
60
131
|
function formatCheckLabel(id) {
|
|
61
132
|
if (id === 'docs')
|
|
62
133
|
return 'docs/: ';
|
|
@@ -15,6 +15,7 @@ function createEvidenceCollectReport(projectRoot, input) {
|
|
|
15
15
|
schemaVersion: 'hadara.evidence.collect.v1',
|
|
16
16
|
command: 'evidence.collect',
|
|
17
17
|
ok: false,
|
|
18
|
+
taskId: input.taskId,
|
|
18
19
|
issues: [
|
|
19
20
|
{
|
|
20
21
|
severity: 'error',
|
|
@@ -49,6 +50,7 @@ function createEvidenceCollectReport(projectRoot, input) {
|
|
|
49
50
|
schemaVersion: 'hadara.evidence.collect.v1',
|
|
50
51
|
command: 'evidence.collect',
|
|
51
52
|
ok: false,
|
|
53
|
+
taskId: input.taskId,
|
|
52
54
|
issues: [
|
|
53
55
|
{
|
|
54
56
|
severity: 'error',
|
|
@@ -64,6 +66,7 @@ function createEvidenceCollectReport(projectRoot, input) {
|
|
|
64
66
|
schemaVersion: 'hadara.evidence.collect.v1',
|
|
65
67
|
command: 'evidence.collect',
|
|
66
68
|
ok: true,
|
|
69
|
+
taskId: input.taskId,
|
|
67
70
|
evidence: {
|
|
68
71
|
...appendResult.evidence,
|
|
69
72
|
markdownPath: toPortablePath(node_path_1.default.relative(projectRoot, appendResult.markdownPath)),
|
package/dist/cli/evidence.js
CHANGED
|
@@ -38,7 +38,7 @@ function handleReleaseArtifactCommand(input) {
|
|
|
38
38
|
})
|
|
39
39
|
: undefined;
|
|
40
40
|
if (input.jsonOutput) {
|
|
41
|
-
console.log(JSON.stringify(attachment ? { ...report, attachedEvidence: attachment } : report, null, 2));
|
|
41
|
+
console.log(JSON.stringify(attachment && taskId ? { ...report, taskId, attachedEvidence: attachment } : report, null, 2));
|
|
42
42
|
}
|
|
43
43
|
else {
|
|
44
44
|
console.log(`${report.ok ? 'passed' : 'failed'} | release artifact | ${report.output.retention}`);
|
package/dist/cli/task.js
CHANGED
|
@@ -12,12 +12,13 @@ const task_finish_1 = require("../task/task-finish");
|
|
|
12
12
|
const task_next_1 = require("../task/task-next");
|
|
13
13
|
const task_upgrade_scaffold_1 = require("../task/task-upgrade-scaffold");
|
|
14
14
|
const task_workbench_1 = require("../services/task-workbench");
|
|
15
|
+
const timing_1 = require("../core/timing");
|
|
15
16
|
const actor_1 = require("./actor");
|
|
16
17
|
const args_1 = require("./args");
|
|
17
18
|
const legacy_boundary_1 = require("./legacy-boundary");
|
|
18
19
|
const task_json_1 = require("./task-json");
|
|
19
20
|
function handleTaskCommand(input) {
|
|
20
|
-
const
|
|
21
|
+
const timer = (0, timing_1.startMonotonicTimer)();
|
|
21
22
|
const sub = input.args[1];
|
|
22
23
|
if (sub === 'create') {
|
|
23
24
|
if (blockLegacyMutation(input, 'task.create'))
|
|
@@ -47,9 +48,9 @@ function handleTaskCommand(input) {
|
|
|
47
48
|
return true;
|
|
48
49
|
}
|
|
49
50
|
if (sub === 'show') {
|
|
50
|
-
const id = input.args[2];
|
|
51
|
-
if (!id)
|
|
52
|
-
throw new Error('task show requires <task-id>');
|
|
51
|
+
const id = (0, args_1.getStringOption)(input.args, '--task') ?? input.args[2];
|
|
52
|
+
if (!id || id.startsWith('--'))
|
|
53
|
+
throw new Error('task show requires --task <task-id>');
|
|
53
54
|
const report = (0, task_json_1.createTaskShowReport)(input.projectRoot, id);
|
|
54
55
|
if (input.jsonOutput) {
|
|
55
56
|
console.log(JSON.stringify(report, null, 2));
|
|
@@ -140,11 +141,15 @@ function handleTaskCommand(input) {
|
|
|
140
141
|
return true;
|
|
141
142
|
}
|
|
142
143
|
if (sub === 'status') {
|
|
144
|
+
const summaryJsonOutput = (0, args_1.getFlag)(input.args, '--summary-json');
|
|
143
145
|
const id = (0, args_1.getStringOption)(input.args, '--task') ?? input.args[2];
|
|
144
146
|
if (!id || id.startsWith('--')) {
|
|
145
147
|
const report = (0, task_workbench_1.createTaskStatusSelectionReport)(input.projectRoot);
|
|
146
|
-
attachCliDiagnostics(report,
|
|
147
|
-
if (
|
|
148
|
+
attachCliDiagnostics(report, timer, 'task.status');
|
|
149
|
+
if (summaryJsonOutput) {
|
|
150
|
+
console.log(JSON.stringify(createTaskStatusSummaryReport(report), null, 2));
|
|
151
|
+
}
|
|
152
|
+
else if (input.jsonOutput) {
|
|
148
153
|
console.log(JSON.stringify(report, null, 2));
|
|
149
154
|
}
|
|
150
155
|
else {
|
|
@@ -158,8 +163,11 @@ function handleTaskCommand(input) {
|
|
|
158
163
|
if (detail && detail !== 'fast' && detail !== 'full')
|
|
159
164
|
throw new Error('task status --detail must be fast or full');
|
|
160
165
|
const report = (0, task_workbench_1.createTaskWorkbenchReport)(input.projectRoot, id, new Date(), { detail: detail === 'full' ? 'full' : 'fast' });
|
|
161
|
-
attachCliDiagnostics(report,
|
|
162
|
-
if (
|
|
166
|
+
attachCliDiagnostics(report, timer, 'task.status');
|
|
167
|
+
if (summaryJsonOutput) {
|
|
168
|
+
console.log(JSON.stringify(createTaskStatusSummaryReport(report), null, 2));
|
|
169
|
+
}
|
|
170
|
+
else if (input.jsonOutput) {
|
|
163
171
|
console.log(JSON.stringify(report, null, 2));
|
|
164
172
|
}
|
|
165
173
|
else {
|
|
@@ -199,7 +207,7 @@ function handleTaskCommand(input) {
|
|
|
199
207
|
actor: (0, actor_1.getActorContextOption)(input.args),
|
|
200
208
|
onProgress: executeRequested ? createTaskFinalizeProgressWriter(id) : undefined
|
|
201
209
|
});
|
|
202
|
-
attachCliDiagnostics(report,
|
|
210
|
+
attachCliDiagnostics(report, timer, 'task.finalize');
|
|
203
211
|
if (input.jsonOutput) {
|
|
204
212
|
console.log(JSON.stringify(report, null, 2));
|
|
205
213
|
}
|
|
@@ -281,14 +289,60 @@ function handleTaskCommand(input) {
|
|
|
281
289
|
}
|
|
282
290
|
return false;
|
|
283
291
|
}
|
|
292
|
+
function createTaskStatusSummaryReport(report) {
|
|
293
|
+
if (report.schemaVersion === 'hadara.task.status.v1') {
|
|
294
|
+
return {
|
|
295
|
+
schemaVersion: 'hadara.task.status.summary.v1',
|
|
296
|
+
command: 'task.status',
|
|
297
|
+
ok: report.ok,
|
|
298
|
+
mode: 'select-work',
|
|
299
|
+
phase: report.loop.phase,
|
|
300
|
+
recommendations: report.summary.recommendations,
|
|
301
|
+
...(report.loop.primaryNextAction ? { primaryNextAction: report.loop.primaryNextAction } : {}),
|
|
302
|
+
...(report.diagnostics ? { diagnostics: report.diagnostics } : {}),
|
|
303
|
+
issues: report.issues
|
|
304
|
+
};
|
|
305
|
+
}
|
|
306
|
+
return {
|
|
307
|
+
schemaVersion: 'hadara.task.status.summary.v1',
|
|
308
|
+
command: 'task.status',
|
|
309
|
+
ok: report.ok,
|
|
310
|
+
mode: 'selected-task',
|
|
311
|
+
taskId: report.taskId,
|
|
312
|
+
task: {
|
|
313
|
+
title: report.task.title,
|
|
314
|
+
capsule: report.task.capsule,
|
|
315
|
+
taskStatus: report.task.taskStatus,
|
|
316
|
+
taskBoardStatus: report.task.taskBoardStatus
|
|
317
|
+
},
|
|
318
|
+
phase: report.loop.phase,
|
|
319
|
+
readiness: {
|
|
320
|
+
status: report.state.readiness.status,
|
|
321
|
+
ready: report.state.ready,
|
|
322
|
+
closeProofValid: report.state.readiness.closeProofValid,
|
|
323
|
+
summary: report.state.readiness.summary
|
|
324
|
+
},
|
|
325
|
+
counts: {
|
|
326
|
+
blockers: report.summary.blockers,
|
|
327
|
+
warnings: report.summary.warnings,
|
|
328
|
+
evidenceRecords: report.summary.evidenceRecords,
|
|
329
|
+
validationChecks: report.sources.evidenceList.validationAttempts?.checks ?? 0,
|
|
330
|
+
unresolvedValidation: report.sources.evidenceList.validationAttempts?.unresolvedFailedOrBlocked ?? 0,
|
|
331
|
+
nextActions: report.summary.nextActions
|
|
332
|
+
},
|
|
333
|
+
...(report.loop.primaryNextAction ? { primaryNextAction: report.loop.primaryNextAction } : {}),
|
|
334
|
+
...(report.diagnostics ? { diagnostics: report.diagnostics } : {}),
|
|
335
|
+
issues: report.issues
|
|
336
|
+
};
|
|
337
|
+
}
|
|
284
338
|
function createTaskFinalizeProgressWriter(taskId) {
|
|
285
339
|
return (event) => {
|
|
286
340
|
const ok = event.ok === undefined ? '' : ` ok=${event.ok}`;
|
|
287
341
|
process.stderr.write(`[HADARA] task finalize ${taskId}: ${event.step} ${event.phase}${ok} - ${event.summary}\n`);
|
|
288
342
|
};
|
|
289
343
|
}
|
|
290
|
-
function attachCliDiagnostics(report,
|
|
291
|
-
const durationMs =
|
|
344
|
+
function attachCliDiagnostics(report, timer, commandPath) {
|
|
345
|
+
const durationMs = timer.elapsedMs();
|
|
292
346
|
const slowThresholdMs = 10000;
|
|
293
347
|
const slow = durationMs >= slowThresholdMs;
|
|
294
348
|
report.diagnostics = {
|
package/dist/cli/validation.js
CHANGED
|
@@ -28,14 +28,24 @@ function handleValidationCommand(input) {
|
|
|
28
28
|
}
|
|
29
29
|
else {
|
|
30
30
|
console.log(`[HADARA] validation run ${taskId}: ${report.result}`);
|
|
31
|
+
console.log(`[HADARA] child command`);
|
|
32
|
+
console.log(`command=${report.argv.join(' ')}`);
|
|
33
|
+
console.log(`exitCode=${report.execution.exitCode ?? 'null'} signal=${report.execution.signal ?? 'null'} durationMs=${report.execution.durationMs}`);
|
|
34
|
+
console.log(`stdoutHash=${report.execution.stdoutHash}`);
|
|
35
|
+
console.log(`stderrHash=${report.execution.stderrHash}`);
|
|
36
|
+
console.log(`childOutput=not printed; stdout/stderr hashes are recorded in HADARA evidence`);
|
|
37
|
+
console.log(`[HADARA] evidence`);
|
|
31
38
|
if (report.evidence)
|
|
32
|
-
console.log(`
|
|
33
|
-
|
|
34
|
-
|
|
39
|
+
console.log(`id=${report.evidence.id}`);
|
|
40
|
+
console.log(`result=${report.result}`);
|
|
41
|
+
console.log(`taskValidationRow=${report.taskValidationRow.mode}${report.taskValidationRow.updated ? ' updated' : ' not-updated'}`);
|
|
42
|
+
console.log(`acceptanceRows=not-updated`);
|
|
35
43
|
for (const issue of report.issues)
|
|
36
44
|
console.log(`[${issue.severity}] ${issue.code}: ${issue.message}`);
|
|
45
|
+
if (report.nextActions.length > 0)
|
|
46
|
+
console.log(`[HADARA] next actions`);
|
|
37
47
|
for (const action of report.nextActions)
|
|
38
|
-
console.log(
|
|
48
|
+
console.log(`${action.id}=${action.command ?? action.message}`);
|
|
39
49
|
}
|
|
40
50
|
if (!report.ok)
|
|
41
51
|
process.exitCode = 6;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.startMonotonicTimer = startMonotonicTimer;
|
|
4
|
+
const node_perf_hooks_1 = require("node:perf_hooks");
|
|
5
|
+
function startMonotonicTimer(now = () => node_perf_hooks_1.performance.now()) {
|
|
6
|
+
const started = now();
|
|
7
|
+
return {
|
|
8
|
+
elapsedMs() {
|
|
9
|
+
return Math.max(0, Math.round(now() - started));
|
|
10
|
+
}
|
|
11
|
+
};
|
|
12
|
+
}
|
package/dist/dev/docker-check.js
CHANGED
|
@@ -9,6 +9,7 @@ const node_child_process_1 = require("node:child_process");
|
|
|
9
9
|
const node_crypto_1 = __importDefault(require("node:crypto"));
|
|
10
10
|
const node_fs_1 = __importDefault(require("node:fs"));
|
|
11
11
|
const node_path_1 = __importDefault(require("node:path"));
|
|
12
|
+
const timing_1 = require("../core/timing");
|
|
12
13
|
const lifecycle_next_actions_1 = require("../task/lifecycle-next-actions");
|
|
13
14
|
function createDevDockerCheckReport(projectRoot, options = {}, runner = defaultRunner) {
|
|
14
15
|
const focusedTests = normalizeFocusedTests(options.focusedTests ?? []);
|
|
@@ -47,9 +48,9 @@ function createDevDockerCheckReport(projectRoot, options = {}, runner = defaultR
|
|
|
47
48
|
steps.push({ id: step.id, status: 'skipped', summary: step.runWhen ? 'Skipped because an earlier step failed.' : step.summary });
|
|
48
49
|
continue;
|
|
49
50
|
}
|
|
50
|
-
const
|
|
51
|
+
const timer = (0, timing_1.startMonotonicTimer)();
|
|
51
52
|
const result = runner.run('docker', dockerExecArgs(container, step.script), env);
|
|
52
|
-
const elapsedMs =
|
|
53
|
+
const elapsedMs = timer.elapsedMs();
|
|
53
54
|
if (result.ok) {
|
|
54
55
|
steps.push({ id: step.id, status: 'passed', elapsedMs, summary: step.summary });
|
|
55
56
|
if (step.mark)
|
|
@@ -20,6 +20,7 @@ const node_path_1 = __importDefault(require("node:path"));
|
|
|
20
20
|
const node_crypto_1 = __importDefault(require("node:crypto"));
|
|
21
21
|
const fs_1 = require("../core/fs");
|
|
22
22
|
const redaction_1 = require("../core/redaction");
|
|
23
|
+
const timing_1 = require("../core/timing");
|
|
23
24
|
const workspace_1 = require("../core/workspace");
|
|
24
25
|
const private_manifest_1 = require("./private-manifest");
|
|
25
26
|
function persistedEvidenceKind(record) {
|
|
@@ -181,7 +182,7 @@ function withEvidenceAppendLock(projectRoot, taskId, fn) {
|
|
|
181
182
|
(0, fs_1.ensureDir)(lockRoot);
|
|
182
183
|
const lockDir = node_path_1.default.join(lockRoot, `${safeFilePart(taskId)}.lock`);
|
|
183
184
|
const lockPortablePath = toPortablePath(node_path_1.default.relative(projectRoot, lockDir));
|
|
184
|
-
const
|
|
185
|
+
const timer = (0, timing_1.startMonotonicTimer)();
|
|
185
186
|
const timeoutMs = 5000;
|
|
186
187
|
while (true) {
|
|
187
188
|
try {
|
|
@@ -191,7 +192,7 @@ function withEvidenceAppendLock(projectRoot, taskId, fn) {
|
|
|
191
192
|
catch (error) {
|
|
192
193
|
if (error.code !== 'EEXIST')
|
|
193
194
|
throw error;
|
|
194
|
-
if (
|
|
195
|
+
if (timer.elapsedMs() >= timeoutMs) {
|
|
195
196
|
throw new EvidenceAppendLockError(`Timed out waiting for the evidence append lock for ${taskId}. Lock directory: ${lockPortablePath}. ` +
|
|
196
197
|
`If no HADARA process is writing evidence, the lock is stale (inspect ${lockPortablePath}/lock.json for the owning pid); remove the lock directory and retry.`);
|
|
197
198
|
}
|
|
@@ -30,6 +30,7 @@ function createHandoffSuggestionReport(projectRoot, taskId, options = {}) {
|
|
|
30
30
|
schemaVersion: 'hadara.handoff.suggestion.v1',
|
|
31
31
|
command: 'handoff.suggest',
|
|
32
32
|
ok: !issues.some((issue) => issue.severity === 'error'),
|
|
33
|
+
taskId,
|
|
33
34
|
readOnly: true,
|
|
34
35
|
generatedAt: new Date().toISOString(),
|
|
35
36
|
actor,
|
package/dist/handoff/handoff.js
CHANGED
|
@@ -49,6 +49,7 @@ Attach test logs, command outputs, and diff summaries under the active Task Caps
|
|
|
49
49
|
schemaVersion: 'hadara.handoff.update.v1',
|
|
50
50
|
command: 'handoff.update',
|
|
51
51
|
ok: true,
|
|
52
|
+
taskId: input.taskId ?? null,
|
|
52
53
|
target: {
|
|
53
54
|
path: 'docs/AGENT_HANDOFF.md',
|
|
54
55
|
writeBoundary: 'shared-doc'
|