hadara 0.3.4-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.
Files changed (83) hide show
  1. package/README.md +29 -29
  2. package/dist/cli/dashboard.js +16 -4
  3. package/dist/cli/docs.js +50 -0
  4. package/dist/cli/doctor.js +74 -3
  5. package/dist/cli/evidence-json.js +3 -0
  6. package/dist/cli/evidence.js +72 -0
  7. package/dist/cli/help.js +4 -3
  8. package/dist/cli/init.js +608 -122
  9. package/dist/cli/legacy-boundary.js +77 -0
  10. package/dist/cli/main.js +28 -0
  11. package/dist/cli/package-smoke.js +1 -0
  12. package/dist/cli/release-artifact.js +10 -1
  13. package/dist/cli/release-publish.js +9 -0
  14. package/dist/cli/task.js +142 -24
  15. package/dist/cli/validation.js +81 -0
  16. package/dist/context/context-pack.js +113 -19
  17. package/dist/context/session-start.js +61 -21
  18. package/dist/context/task-extractors.js +3 -2
  19. package/dist/core/schema.js +14 -0
  20. package/dist/core/timing.js +12 -0
  21. package/dist/dev/docker-check.js +3 -2
  22. package/dist/evidence/evidence.js +134 -11
  23. package/dist/evidence/semantics.js +3 -1
  24. package/dist/handoff/handoff-stale-problems.js +6 -1
  25. package/dist/handoff/handoff-suggestion.js +7 -1
  26. package/dist/handoff/handoff.js +1 -0
  27. package/dist/harness/validate.js +434 -72
  28. package/dist/schemas/close-source.schema.json +33 -0
  29. package/dist/schemas/docs-inbox.schema.json +50 -0
  30. package/dist/schemas/docs-read-map.schema.json +83 -0
  31. package/dist/schemas/docs-register.schema.json +70 -0
  32. package/dist/schemas/evidence-projection.schema.json +35 -0
  33. package/dist/schemas/schema-index.json +51 -2
  34. package/dist/schemas/task-audit-close.schema.json +7 -0
  35. package/dist/schemas/task-close.schema.json +4 -0
  36. package/dist/schemas/task-finalize.schema.json +40 -1
  37. package/dist/schemas/task-status.schema.json +117 -0
  38. package/dist/schemas/task-workbench.schema.json +97 -2
  39. package/dist/schemas/validation-run.schema.json +103 -0
  40. package/dist/services/capability-registry.js +216 -51
  41. package/dist/services/ci-gate.js +5 -2
  42. package/dist/services/clean-checkout-smoke.js +4 -2
  43. package/dist/services/dashboard-bootstrap.js +19 -4
  44. package/dist/services/dashboard-cache.js +2 -0
  45. package/dist/services/dashboard-task-detail.js +43 -13
  46. package/dist/services/dashboard-timeline.js +2 -2
  47. package/dist/services/docs-registry.js +485 -30
  48. package/dist/services/evidence-lint.js +9 -3
  49. package/dist/services/lifecycle-guide.js +11 -12
  50. package/dist/services/operational-debt.js +8 -14
  51. package/dist/services/operations-status-service.js +7 -1
  52. package/dist/services/package-recycle.js +101 -45
  53. package/dist/services/package-smoke.js +7 -2
  54. package/dist/services/proof-status.js +2 -0
  55. package/dist/services/protocol-consistency.js +13 -6
  56. package/dist/services/protocol-migration.js +2 -1
  57. package/dist/services/release-artifact.js +3 -2
  58. package/dist/services/release-closeout.js +2 -1
  59. package/dist/services/release-diagnostics.js +5 -4
  60. package/dist/services/release-dry-run.js +3 -2
  61. package/dist/services/state-projection.js +31 -16
  62. package/dist/services/task-read-model.js +7 -7
  63. package/dist/services/task-workbench.js +598 -29
  64. package/dist/services/validation-run.js +333 -0
  65. package/dist/services/workbench-next-actions.js +31 -20
  66. package/dist/services/write-preflight.js +2 -20
  67. package/dist/task/acceptance.js +27 -5
  68. package/dist/task/authoring-guidance.js +76 -0
  69. package/dist/task/lifecycle-next-actions.js +0 -1
  70. package/dist/task/task-capsule.js +24 -18
  71. package/dist/task/task-close-repair-plan.js +3 -2
  72. package/dist/task/task-close.js +319 -39
  73. package/dist/task/task-complete-flow.js +4 -3
  74. package/dist/task/task-create.js +1 -0
  75. package/dist/task/task-finalize.js +161 -28
  76. package/dist/task/task-finish.js +36 -8
  77. package/dist/task/task-lifecycle.js +8 -3
  78. package/dist/task/task-templates.js +5 -24
  79. package/dist/task/task-upgrade-scaffold.js +11 -60
  80. package/dist/tui/constants.js +1 -7
  81. package/dist/tui/read-model.js +6 -4
  82. package/dist/tui/snapshot.js +14 -2
  83. 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.3-blue">
9
- <img alt="Source version" src="https://img.shields.io/badge/source-0.3.4--rc.0-blue">
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.3.3
27
+ hadara@0.4.0
28
28
  ```
29
29
 
30
- Current source release candidate:
30
+ Previous npm release candidate:
31
31
 
32
32
  ```text
33
- hadara@0.3.4-rc.0
33
+ hadara@0.4.0-rc.0
34
34
  ```
35
35
 
36
- Previous published release candidate:
36
+ Previous agent UX release candidate:
37
37
 
38
38
  ```text
39
- hadara@0.3.3-rc.0
39
+ hadara@0.3.4-rc.0
40
40
  ```
41
41
 
42
- The 0.3.4 source line is an agent UX hardening release candidate after stable 0.3.3. It keeps the context-routing and finalize-first lifecycle surfaces, then improves the commands agents actually use during HADARA development: session start, context pack, evidence summary, handoff stale-problem checks, release closeout, package recycle, generated init guidance, and close/finalize diagnostics.
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.3.3` package is the default install target; `0.3.4-rc.0` is the current source candidate and must not be treated as published until an approval-gated publish capsule completes.
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.3.3`](docs/RELEASE_NOTES.md#033) |
49
- | Source candidate | [`hadara@0.3.4-rc.0`](docs/RELEASE_NOTES.md#034-rc0) |
50
- | Previous RC | [`hadara@0.3.3-rc.0`](docs/RELEASE_NOTES.md#033-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.3.3` is the npm package line until the `0.3.4-rc.0` publish capsule completes; GitHub Release, Docker/PyPI publish, installer execution, and MCP release/package execution remain separate explicit mutations.
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.3.3
67
+ npm install -g hadara@0.4.0
68
68
  hadara help
69
69
  hadara doctor --json
70
70
  ```
@@ -72,26 +72,26 @@ hadara doctor --json
72
72
  Run without a global install:
73
73
 
74
74
  ```bash
75
- npx hadara@0.3.3 help
76
- npx hadara@0.3.3 doctor --json
75
+ npx hadara@0.4.0 help
76
+ npx hadara@0.4.0 doctor --json
77
77
  ```
78
78
 
79
- Evaluate the previous release candidate explicitly:
79
+ Evaluate the previous `0.4.0-rc.0` explicitly:
80
80
 
81
81
  ```bash
82
- npm install -g hadara@0.3.3-rc.0
83
- npx hadara@0.3.3-rc.0 help
82
+ npm install -g hadara@0.4.0-rc.0
83
+ npx hadara@0.4.0-rc.0 help
84
84
  ```
85
85
 
86
86
  For release or recycle evidence, prefer an isolated prefix install when PATH, global installs, or `npx` cache behavior may be stale:
87
87
 
88
88
  ```bash
89
89
  tmp="$(mktemp -d)"
90
- npm --prefix "$tmp" install hadara@0.3.3
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.3.3 ...` 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.
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
 
@@ -116,7 +116,7 @@ Ask the CLI for the workflow before choosing commands:
116
116
  ```bash
117
117
  hadara help
118
118
  hadara help lifecycle
119
- hadara task next --json
119
+ hadara task status --json
120
120
  ```
121
121
 
122
122
  Use structured discovery when an agent or tool needs machine-readable command metadata:
@@ -129,16 +129,15 @@ hadara help command task.close
129
129
 
130
130
  ## Primary Capsule Lifecycle
131
131
 
132
- The 0.3.4 primary path is intentionally small. Agents should start with compact context and use `task finalize` as the default lifecycle close path:
132
+ The 0.4 primary path is intentionally small. Agents should start with compact context and use `task finalize` as the default lifecycle close path:
133
133
 
134
134
  ```bash
135
- hadara task next --json
135
+ hadara task status --json
136
136
  hadara session start --task T-XXXX --json
137
137
  hadara task create "implement a focused change" --json
138
138
  hadara task status --task T-XXXX --json
139
139
  hadara evidence add-command --task T-XXXX --summary "..." --result passed --category validation --idempotency-key "command:T-XXXX:check" --json
140
140
  # Finalize Task Capsule docs and tracked state docs before closing.
141
- hadara task lifecycle --task T-XXXX --json
142
141
  hadara task finalize --task T-XXXX --json
143
142
  hadara task finalize --task T-XXXX --execute --plan-hash sha256:... --json
144
143
  hadara handoff suggest --task T-XXXX --json
@@ -162,8 +161,9 @@ Deferred Evidence v2 scope is explicit: rebuild preview/execute, `check-id`, `su
162
161
  Use read-only lifecycle diagnostics when you want a compact current-stage report, next recommended action, or close-proof repair explanation:
163
162
 
164
163
  ```bash
164
+ hadara task status --task T-XXXX --json
165
165
  hadara task complete --task T-XXXX --json
166
- hadara task lifecycle --task T-XXXX --json
166
+ hadara task lifecycle --task T-XXXX --json # compatibility
167
167
  hadara task close-repair-plan --task T-XXXX --json
168
168
  ```
169
169
 
@@ -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.3.4-rc.0 is not:
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.3.4-rc.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.
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
 
@@ -321,7 +321,7 @@ hadara init --profile standard
321
321
  hadara init --profile governed
322
322
  ```
323
323
 
324
- Every profile generates `docs/TASK_WORKFLOW_COMMANDS.md` so fresh projects get the current 0.3.4 context-aware finalize-first lifecycle loop plus low-level proof-boundary reference commands.
324
+ Every profile generates the 0.4 project scaffold with compact agent entry docs, a docs registry, a workflow guide, and the current context-aware finalize-first lifecycle loop plus low-level proof-boundary reference commands.
325
325
 
326
326
  | Profile | Use When |
327
327
  |---|---|
@@ -95,7 +95,7 @@ function createDashboardApiResponse(projectRoot, requestUrl, method) {
95
95
  if (url.pathname === '/api/status') {
96
96
  // Dashboard clients fetch operational debt separately; keep the status
97
97
  // route cheap so a single slow debt scan cannot block first API paint.
98
- return jsonResponse((0, operations_status_service_1.createOpsStatusReport)(projectRoot, { includeDebt: false }), headOnly);
98
+ return jsonResponse(readCachedOpsStatus(projectRoot, false, url.searchParams.get('cache') === 'bypass'), headOnly);
99
99
  }
100
100
  if (url.pathname === '/api/dashboard/core') {
101
101
  const bypassProjection = url.searchParams.get('cache') === 'bypass';
@@ -125,7 +125,12 @@ function createDashboardApiResponse(projectRoot, requestUrl, method) {
125
125
  if (selectedTaskId)
126
126
  keyParts.push('selected', selectedTaskId);
127
127
  const key = (0, dashboard_cache_1.createDashboardCacheKey)(projectRoot, ...keyParts);
128
- const cached = (0, dashboard_cache_1.getOrCreateCachedReport)(key, { ttlMs: dashboard_cache_1.DASHBOARD_CACHE_TTLS.bootstrap, bypass: url.searchParams.get('cache') === 'bypass' }, () => (0, dashboard_bootstrap_1.createDashboardBootstrapReport)(projectRoot, { tier, ...(selectedTaskId ? { selectedTaskId } : {}) }));
128
+ const cached = (0, dashboard_cache_1.getOrCreateCachedReport)(key, { ttlMs: dashboard_cache_1.DASHBOARD_CACHE_TTLS.bootstrap, bypass: url.searchParams.get('cache') === 'bypass' }, () => (0, dashboard_bootstrap_1.createDashboardBootstrapReport)(projectRoot, {
129
+ tier,
130
+ ...(selectedTaskId ? { selectedTaskId } : {}),
131
+ status: readCachedOpsStatus(projectRoot, tier === 'full', url.searchParams.get('cache') === 'bypass'),
132
+ tasks: readCachedTaskList(projectRoot, url.searchParams.get('cache') === 'bypass')
133
+ }));
129
134
  return jsonResponse((0, dashboard_cache_1.withDashboardCacheMetadata)(cached.value, cached.cache), headOnly);
130
135
  }
131
136
  if (url.pathname === '/api/dashboard/task-detail') {
@@ -139,7 +144,7 @@ function createDashboardApiResponse(projectRoot, requestUrl, method) {
139
144
  if (url.pathname === '/api/dashboard/cache/status')
140
145
  return jsonResponse((0, dashboard_cache_1.createDashboardCacheStatusReport)(), headOnly);
141
146
  if (url.pathname === '/api/tasks')
142
- return jsonResponse((0, task_read_model_1.createTaskListReport)(projectRoot), headOnly);
147
+ return jsonResponse(readCachedTaskList(projectRoot, url.searchParams.get('cache') === 'bypass'), headOnly);
143
148
  if (url.pathname === '/api/active-run')
144
149
  return jsonResponse((0, active_run_state_1.safeCreateActiveRunProjection)(projectRoot), headOnly);
145
150
  if (url.pathname === '/api/debt') {
@@ -159,7 +164,8 @@ function createDashboardApiResponse(projectRoot, requestUrl, method) {
159
164
  const taskId = url.searchParams.get('taskId')?.trim();
160
165
  if (!taskId)
161
166
  return missingTaskId(headOnly);
162
- return jsonResponse((0, task_workbench_1.createTaskWorkbenchReport)(projectRoot, taskId), headOnly);
167
+ const detail = url.searchParams.get('detail') === 'full' ? 'full' : 'fast';
168
+ return jsonResponse((0, task_workbench_1.createTaskWorkbenchReport)(projectRoot, taskId, new Date(), { detail }), headOnly);
163
169
  }
164
170
  if (url.pathname === '/api/evidence-lint') {
165
171
  const taskId = url.searchParams.get('taskId')?.trim();
@@ -175,6 +181,12 @@ function createDashboardApiResponse(projectRoot, requestUrl, method) {
175
181
  }
176
182
  return notFound();
177
183
  }
184
+ function readCachedOpsStatus(projectRoot, includeDebt, bypass) {
185
+ return (0, dashboard_cache_1.getOrCreateCachedReport)((0, dashboard_cache_1.createDashboardCacheKey)(projectRoot, 'status', includeDebt ? 'full' : 'core'), { ttlMs: dashboard_cache_1.DASHBOARD_CACHE_TTLS.status, bypass }, () => (0, operations_status_service_1.createOpsStatusReport)(projectRoot, { includeDebt })).value;
186
+ }
187
+ function readCachedTaskList(projectRoot, bypass) {
188
+ return (0, dashboard_cache_1.getOrCreateCachedReport)((0, dashboard_cache_1.createDashboardCacheKey)(projectRoot, 'tasks'), { ttlMs: dashboard_cache_1.DASHBOARD_CACHE_TTLS.tasks, bypass }, () => (0, task_read_model_1.createTaskListReport)(projectRoot)).value;
189
+ }
178
190
  function missingTaskId(headOnly) {
179
191
  return jsonResponse({
180
192
  schemaVersion: 'hadara.dashboard.api.error.v1',
package/dist/cli/docs.js CHANGED
@@ -5,6 +5,7 @@ const args_1 = require("./args");
5
5
  const docs_cleanup_1 = require("../services/docs-cleanup");
6
6
  const docs_registry_1 = require("../services/docs-registry");
7
7
  const managed_sections_1 = require("../services/managed-sections");
8
+ const legacy_boundary_1 = require("./legacy-boundary");
8
9
  function handleDocsCommand(input) {
9
10
  const sub = input.args[1];
10
11
  if (sub === 'list') {
@@ -26,6 +27,39 @@ function handleDocsCommand(input) {
26
27
  printReport(report, input.jsonOutput);
27
28
  return true;
28
29
  }
30
+ if (sub === 'read-map') {
31
+ const report = (0, docs_registry_1.createDocsReadMapReport)(input.projectRoot, (0, args_1.getRequiredStringOption)(input.args, '--task'));
32
+ printReport(report, input.jsonOutput);
33
+ return true;
34
+ }
35
+ if (sub === 'inbox') {
36
+ const report = (0, docs_registry_1.createDocsInboxReport)(input.projectRoot);
37
+ printReport(report, input.jsonOutput);
38
+ return true;
39
+ }
40
+ if (sub === 'register') {
41
+ if ((0, args_1.getFlag)(input.args, '--execute') && blockLegacyMutation(input, 'docs.register'))
42
+ return true;
43
+ const report = (0, docs_registry_1.createDocsRegisterReport)(input.projectRoot, {
44
+ documentPath: (0, args_1.getRequiredStringOption)(input.args, '--path'),
45
+ title: (0, args_1.getStringOption)(input.args, '--title'),
46
+ kind: (0, args_1.getStringOption)(input.args, '--kind'),
47
+ status: (0, args_1.getStringOption)(input.args, '--status'),
48
+ readWhen: (0, args_1.getStringOption)(input.args, '--read-when'),
49
+ readTier: (0, args_1.getStringOption)(input.args, '--read-tier'),
50
+ authority: (0, args_1.getStringOption)(input.args, '--authority'),
51
+ editPolicy: (0, args_1.getStringOption)(input.args, '--edit-policy'),
52
+ activeForTasks: splitCsv((0, args_1.getStringOption)(input.args, '--active-for-task')),
53
+ driftRisk: (0, args_1.getStringOption)(input.args, '--drift'),
54
+ driftReviewRequired: (0, args_1.getFlag)(input.args, '--drift-review-required'),
55
+ driftReason: (0, args_1.getStringOption)(input.args, '--drift-reason'),
56
+ requiredReading: (0, args_1.getFlag)(input.args, '--required-reading'),
57
+ requireExists: (0, args_1.getFlag)(input.args, '--require-exists'),
58
+ mode: (0, args_1.getFlag)(input.args, '--execute') ? 'execute' : 'dry-run'
59
+ });
60
+ printReport(report, input.jsonOutput);
61
+ return true;
62
+ }
29
63
  if (sub === 'managed') {
30
64
  const managedSub = input.args[2];
31
65
  if (managedSub === 'list') {
@@ -38,6 +72,8 @@ function handleDocsCommand(input) {
38
72
  }
39
73
  }
40
74
  if (sub === 'patch') {
75
+ if ((0, args_1.getFlag)(input.args, '--execute') && blockLegacyMutation(input, 'docs.patch'))
76
+ return true;
41
77
  const report = (0, managed_sections_1.createDocsPatchPlanReport)(input.projectRoot, {
42
78
  targetPath: (0, args_1.getRequiredStringOption)(input.args, '--path'),
43
79
  sectionId: (0, args_1.getRequiredStringOption)(input.args, '--section'),
@@ -72,6 +108,14 @@ function handleDocsCommand(input) {
72
108
  }
73
109
  return false;
74
110
  }
111
+ function blockLegacyMutation(input, command) {
112
+ const report = (0, legacy_boundary_1.createLegacyMutationBlockedReport)(input.projectRoot, command);
113
+ if (!report)
114
+ return false;
115
+ (0, legacy_boundary_1.printLegacyMutationBlockedReport)(report, input.jsonOutput);
116
+ process.exitCode = 6;
117
+ return true;
118
+ }
75
119
  function printReport(report, jsonOutput) {
76
120
  if (jsonOutput) {
77
121
  console.log(JSON.stringify(report, null, 2));
@@ -79,3 +123,9 @@ function printReport(report, jsonOutput) {
79
123
  }
80
124
  console.log(JSON.stringify(report, null, 2));
81
125
  }
126
+ function splitCsv(value) {
127
+ if (value === undefined)
128
+ return undefined;
129
+ const items = value.split(',').map((item) => item.trim()).filter(Boolean);
130
+ return items.length > 0 ? items : undefined;
131
+ }
@@ -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
- function createDoctorReport(paths, nodeVersion = process.version) {
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)),
@@ -13,8 +13,13 @@ const evidence_list_1 = require("../services/evidence-list");
13
13
  const evidence_migration_1 = require("../services/evidence-migration");
14
14
  const evidence_summary_1 = require("../services/evidence-summary");
15
15
  const args_1 = require("./args");
16
+ const legacy_boundary_1 = require("./legacy-boundary");
16
17
  function handleEvidenceCommand(input) {
17
18
  const sub = input.args[1];
19
+ if (hasHelpFlag(input.args)) {
20
+ console.log(renderEvidenceCommandHelp(sub));
21
+ return true;
22
+ }
18
23
  if (sub === 'list') {
19
24
  const taskId = (0, args_1.getRequiredStringOption)(input.args, '--task');
20
25
  const report = (0, evidence_list_1.createEvidenceListReport)(input.projectRoot, {
@@ -79,7 +84,24 @@ function handleEvidenceCommand(input) {
79
84
  process.exitCode = 6;
80
85
  return true;
81
86
  }
87
+ if (sub === 'project') {
88
+ const taskId = (0, args_1.getRequiredStringOption)(input.args, '--task');
89
+ const report = (0, evidence_1.createEvidenceProjectionReport)(input.projectRoot, taskId, (0, args_1.getFlag)(input.args, '--execute'));
90
+ if (input.jsonOutput) {
91
+ console.log(JSON.stringify(report, null, 2));
92
+ }
93
+ else {
94
+ console.log(`[HADARA] evidence project ${taskId}: ${report.ok ? 'ok' : 'issues'} | mode ${report.mode} | wouldChange ${report.wouldChange}`);
95
+ for (const issue of report.issues)
96
+ console.log(`[${issue.severity}] ${issue.code}: ${issue.message}`);
97
+ }
98
+ if (!report.ok)
99
+ process.exitCode = 6;
100
+ return true;
101
+ }
82
102
  if (sub === 'migrate') {
103
+ if ((0, args_1.getFlag)(input.args, '--execute') && blockLegacyMutation(input, 'evidence.migrate'))
104
+ return true;
83
105
  const taskId = (0, args_1.getRequiredStringOption)(input.args, '--task');
84
106
  const report = (0, evidence_migration_1.createEvidenceMigrationPreviewReport)({
85
107
  projectRoot: input.projectRoot,
@@ -103,6 +125,8 @@ function handleEvidenceCommand(input) {
103
125
  return true;
104
126
  }
105
127
  if (sub === 'add-command') {
128
+ if (blockLegacyMutation(input, 'evidence.add-command'))
129
+ return true;
106
130
  const taskId = (0, args_1.getRequiredStringOption)(input.args, '--task');
107
131
  const summary = (0, args_1.getStringOption)(input.args, '--summary') ?? 'Command completed.';
108
132
  const outcome = parseOptionalEvidenceOutcome((0, args_1.getStringOption)(input.args, '--outcome'));
@@ -114,6 +138,7 @@ function handleEvidenceCommand(input) {
114
138
  schemaVersion: 'hadara.evidence.collect.v1',
115
139
  command: 'evidence.add-command',
116
140
  ok: false,
141
+ taskId,
117
142
  issues: [resultOutcomeIssue]
118
143
  };
119
144
  if (input.jsonOutput) {
@@ -158,6 +183,8 @@ function handleEvidenceCommand(input) {
158
183
  }
159
184
  if (sub !== 'collect')
160
185
  return false;
186
+ if (blockLegacyMutation(input, 'evidence.collect'))
187
+ return true;
161
188
  const taskId = (0, args_1.getRequiredStringOption)(input.args, '--task');
162
189
  const kind = parseEvidenceKind((0, args_1.getStringOption)(input.args, '--kind', 'note') ?? 'note');
163
190
  const summary = (0, args_1.getStringOption)(input.args, '--summary') ?? 'Manual evidence collection placeholder.';
@@ -190,6 +217,51 @@ function handleEvidenceCommand(input) {
190
217
  }
191
218
  return true;
192
219
  }
220
+ function hasHelpFlag(args) {
221
+ return args.includes('--help') || args.includes('-h');
222
+ }
223
+ function renderEvidenceCommandHelp(sub) {
224
+ if (sub === 'add-command') {
225
+ return [
226
+ '[HADARA] evidence add-command',
227
+ '',
228
+ 'Usage:',
229
+ ' hadara evidence add-command --task T-XXXX --summary "..." --result passed --category validation --json',
230
+ '',
231
+ 'Records an already-run command result. It does not execute shell commands.',
232
+ 'Options:',
233
+ ' --task <id> Task Capsule id.',
234
+ ' --summary <text> Human-readable result summary.',
235
+ ' --result <value> Legacy-compatible result: passed, failed, blocked, unknown.',
236
+ ' --outcome <value> Evidence v2 outcome.',
237
+ ' --category <value> Evidence v2 category.',
238
+ ' --resolves <id> Add a resolves:<id> tag. Repeatable.',
239
+ ' --supersedes <id> Add a supersedes:<id> tag. Repeatable.',
240
+ ' --idempotency-key <key> Reuse an existing keyed record instead of appending duplicates.',
241
+ ' --private Store as private evidence metadata.'
242
+ ].join('\n');
243
+ }
244
+ return [
245
+ '[HADARA] evidence',
246
+ '',
247
+ 'Usage:',
248
+ ' hadara evidence list --task T-XXXX --json',
249
+ ' hadara evidence lint --task T-XXXX --json',
250
+ ' hadara evidence summary --task T-XXXX --json',
251
+ ' hadara evidence project --task T-XXXX [--execute] --json',
252
+ ' hadara evidence add-command --task T-XXXX --summary "..." --result passed --json',
253
+ '',
254
+ 'Use `hadara help command evidence.add-command` for registry metadata.'
255
+ ].join('\n');
256
+ }
257
+ function blockLegacyMutation(input, command) {
258
+ const report = (0, legacy_boundary_1.createLegacyMutationBlockedReport)(input.projectRoot, command);
259
+ if (!report)
260
+ return false;
261
+ (0, legacy_boundary_1.printLegacyMutationBlockedReport)(report, input.jsonOutput);
262
+ process.exitCode = 6;
263
+ return true;
264
+ }
193
265
  function persistedEvidenceId(record) {
194
266
  return record.schemaVersion === 'hadara.evidence.v2' && record.id ? record.id : 'evidence.jsonl';
195
267
  }
package/dist/cli/help.js CHANGED
@@ -7,7 +7,7 @@ exports.renderCommandHelp = renderCommandHelp;
7
7
  exports.renderFamilyHelp = renderFamilyHelp;
8
8
  const capability_registry_1 = require("../services/capability-registry");
9
9
  const lifecycle_guide_1 = require("../services/lifecycle-guide");
10
- const LIFECYCLE_ORDER = ['discover', 'create', 'inspect', 'evidence', 'phase-check', 'finalize', 'handoff'];
10
+ const LIFECYCLE_ORDER = ['inspect', 'create', 'evidence', 'finalize', 'handoff'];
11
11
  function handleHelpCommand(input) {
12
12
  const topic = input.args[1];
13
13
  if (!topic) {
@@ -51,7 +51,7 @@ function renderDefaultHelp() {
51
51
  '',
52
52
  'Start:',
53
53
  ' hadara help lifecycle',
54
- ' hadara task next --json',
54
+ ' hadara task status --json',
55
55
  ' hadara task status --task T-XXXX --json',
56
56
  '',
57
57
  'Primary capsule lifecycle:',
@@ -74,7 +74,7 @@ function renderLifecycleHelp() {
74
74
  const report = (0, lifecycle_guide_1.createLifecycleGuideReport)();
75
75
  const diagnostics = (0, capability_registry_1.listCommandRegistryEntries)({ family: 'proof-diagnostics' }).filter((entry) => entry.requiredness === 'diagnostic');
76
76
  const lines = [
77
- 'HADARA 0.3.3 primary task lifecycle',
77
+ 'HADARA 0.4 primary task lifecycle',
78
78
  '',
79
79
  'Primary capsule lifecycle:'
80
80
  ];
@@ -104,6 +104,7 @@ function renderCommandHelp(id) {
104
104
  `Scope: ${entry.scope}`,
105
105
  `Lifecycle stage: ${entry.lifecycleStage}`,
106
106
  `Requiredness: ${entry.requiredness}`,
107
+ `Status: ${entry.status}`,
107
108
  `Write boundary: ${entry.writeBoundary}`,
108
109
  `Read-only: ${entry.readOnly ? 'yes' : 'no'}`,
109
110
  `Risk: ${entry.risk}`,