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
@@ -10,6 +10,7 @@ const node_path_1 = __importDefault(require("node:path"));
10
10
  const context_graph_builder_1 = require("./context-graph-builder");
11
11
  const context_slice_boundary_1 = require("./context-slice-boundary");
12
12
  const extractor_contract_1 = require("./extractor-contract");
13
+ const docs_registry_1 = require("../services/docs-registry");
13
14
  exports.CONTEXT_PACK_SCHEMA_ID = 'hadara.contextPack.v1';
14
15
  exports.CONTEXT_PACK_COMMAND = 'context.pack';
15
16
  exports.CONTEXT_PACK_DEFAULT_BUDGET = {
@@ -62,31 +63,43 @@ function buildContextPackReport(input) {
62
63
  });
63
64
  }
64
65
  const connectedIds = taskId ? connectedNodeIds(graphReport.edges, (0, extractor_contract_1.createTaskNodeId)(taskId)) : new Set();
66
+ const docsReadMap = resolveDocsReadMap(input, taskId);
67
+ const readMapExcludedPaths = new Set((docsReadMap?.doNotReadByDefault ?? []).map((entry) => entry.path));
65
68
  const rankedNodes = rankContextPackNodes(graphReport.nodes, graphReport.edges, connectedIds, taskNode);
66
- const readFirstRanked = rankedNodes.filter((ranked) => isReadFirstAllowed(ranked.node));
67
- const readFirst = readFirstRanked
68
- .slice(0, budget.maxReadFirstItems)
69
- .map((ranked) => itemFromRankedNode(ranked, true, input.projectRoot));
70
- if (readFirstRanked.length > readFirst.length) {
69
+ const readFirstRanked = rankedNodes
70
+ .filter((ranked) => isReadFirstAllowed(ranked.node))
71
+ .filter((ranked) => !isExcludedByReadMap(ranked.node, readMapExcludedPaths));
72
+ const graphReadFirst = readFirstRanked.map((ranked) => itemFromRankedNode(ranked, true, input.projectRoot));
73
+ const readFirstCandidates = mergeContextPackItems([
74
+ ...graphReadFirst.filter((item) => item.id === `task:${taskId}`),
75
+ ...readMapItems(docsReadMap?.readFirst ?? [], input.projectRoot, true),
76
+ ...graphReadFirst.filter((item) => item.id !== `task:${taskId}`)
77
+ ]);
78
+ const readFirst = readFirstCandidates.slice(0, budget.maxReadFirstItems);
79
+ if (readFirstCandidates.length > readFirst.length) {
71
80
  issues.push({
72
81
  severity: 'warning',
73
82
  code: 'CONTEXT_PACK_BUDGET_TRUNCATED',
74
- message: `Context pack readFirst items were truncated from ${readFirstRanked.length} to ${readFirst.length}.`
83
+ message: `Context pack readFirst items were truncated from ${readFirstCandidates.length} to ${readFirst.length}.`
75
84
  });
76
85
  }
77
86
  const selectedIds = new Set(readFirst.map((item) => item.id));
87
+ const selectedPaths = new Set(readFirst.map((item) => item.path).filter((value) => Boolean(value)));
78
88
  const maxReadIfNeeded = Math.max(0, (budget.maxItems ?? 30) - readFirst.length);
79
89
  const readIfNeededRanked = rankedNodes
80
- .filter((ranked) => !selectedIds.has(ranked.node.id))
90
+ .filter((ranked) => !selectedIds.has(ranked.node.id) && (!ranked.node.path || !selectedPaths.has(ranked.node.path)))
91
+ .filter((ranked) => !isExcludedByReadMap(ranked.node, readMapExcludedPaths))
81
92
  .filter((ranked) => !isDoNotReadByDefault(ranked.node));
82
- const readIfNeeded = readIfNeededRanked
83
- .slice(0, maxReadIfNeeded)
84
- .map((ranked) => itemFromRankedNode(ranked, false, input.projectRoot));
85
- if (readIfNeededRanked.length > readIfNeeded.length) {
93
+ const readIfNeededCandidates = mergeContextPackItems([
94
+ ...readMapItems(docsReadMap?.readIfNeeded ?? [], input.projectRoot, false),
95
+ ...readIfNeededRanked.map((ranked) => itemFromRankedNode(ranked, false, input.projectRoot))
96
+ ]).filter((item) => !selectedIds.has(item.id) && (!item.path || !selectedPaths.has(item.path)));
97
+ const readIfNeeded = readIfNeededCandidates.slice(0, maxReadIfNeeded);
98
+ if (readIfNeededCandidates.length > readIfNeeded.length) {
86
99
  issues.push({
87
100
  severity: 'warning',
88
101
  code: 'CONTEXT_PACK_BUDGET_TRUNCATED',
89
- message: `Context pack readIfNeeded items were truncated from ${readIfNeededRanked.length} to ${readIfNeeded.length}.`
102
+ message: `Context pack readIfNeeded items were truncated from ${readIfNeededCandidates.length} to ${readIfNeeded.length}.`
90
103
  });
91
104
  }
92
105
  const doNotReadByDefault = graphReport.nodes
@@ -137,16 +150,86 @@ function buildContextPackReport(input) {
137
150
  graphAvailable: true,
138
151
  codeIndexAvailable: codeIndexAvailable(graphReport),
139
152
  stateProjectionAvailable: true,
140
- docsRegistryAvailable: graphReport.stateProjection.sources.some((source) => source.kind === 'docs-registry'),
153
+ docsRegistryAvailable: Boolean(docsReadMap?.source.registryPresent) || graphReport.stateProjection.sources.some((source) => source.kind === 'docs-registry'),
141
154
  commandRegistryAvailable: graphReport.nodes.some((node) => node.type === 'Command'),
142
155
  degraded: graphReport.summary.degraded || issues.some((issue) => issue.severity !== 'info'),
143
156
  graphSourceHash: graphReport.sourceHash,
144
- sourcesRead: graphReport.summary.sourcesRead
157
+ sourcesRead: graphReport.summary.sourcesRead,
158
+ docsReadMapAvailable: Boolean(docsReadMap),
159
+ docsReadMapReadFirstCount: docsReadMap?.readFirst.length ?? 0,
160
+ docsReadMapDoNotReadByDefaultCount: docsReadMap?.doNotReadByDefault.length ?? 0
145
161
  },
146
162
  cache,
147
163
  issues
148
164
  };
149
165
  }
166
+ function resolveDocsReadMap(input, taskId) {
167
+ if (input.docsReadMap)
168
+ return input.docsReadMap;
169
+ if (input.graphReport || !taskId)
170
+ return undefined;
171
+ if (!node_fs_1.default.existsSync(node_path_1.default.join(input.projectRoot, '.hadara', 'docs-registry.json')))
172
+ return undefined;
173
+ return (0, docs_registry_1.createDocsReadMapReport)(input.projectRoot, taskId);
174
+ }
175
+ function readMapItems(entries, projectRoot, required) {
176
+ return [...entries].sort(compareReadMapEntriesForContextPack).map((entry) => {
177
+ const sourceHash = sourceHashForPath(projectRoot, entry.path);
178
+ return {
179
+ id: `doc:${entry.path}`,
180
+ type: 'Document',
181
+ path: entry.path,
182
+ title: entry.title,
183
+ reason: `Docs read-map ${entry.readTier}: ${entry.reason}`,
184
+ confidence: entry.readTier === 'active-task' || entry.readTier === 'active-spec' ? 'explicit' : 'derived',
185
+ ...(sourceHash ? { sourceHash } : {}),
186
+ estimatedTokens: estimateTokensForPath(entry.path, entry.title),
187
+ required,
188
+ sourceAccess: sourceAccessForPath(entry.path)
189
+ };
190
+ });
191
+ }
192
+ function compareReadMapEntriesForContextPack(a, b) {
193
+ return readMapTierPriority(a.readTier) - readMapTierPriority(b.readTier) || a.path.localeCompare(b.path);
194
+ }
195
+ function readMapTierPriority(tier) {
196
+ if (tier === 'active-task')
197
+ return 0;
198
+ if (tier === 'active-spec')
199
+ return 1;
200
+ if (tier === 'current-state')
201
+ return 2;
202
+ if (tier === 'workflow-reference')
203
+ return 3;
204
+ if (tier === 'conditional-reference')
205
+ return 4;
206
+ if (tier === 'implemented-reference')
207
+ return 5;
208
+ if (tier === 'drift-review')
209
+ return 6;
210
+ if (tier === 'historical')
211
+ return 7;
212
+ return 8;
213
+ }
214
+ function mergeContextPackItems(items) {
215
+ const seenIds = new Set();
216
+ const seenPaths = new Set();
217
+ const merged = [];
218
+ for (const item of items) {
219
+ if (seenIds.has(item.id))
220
+ continue;
221
+ if (item.path && seenPaths.has(item.path))
222
+ continue;
223
+ seenIds.add(item.id);
224
+ if (item.path)
225
+ seenPaths.add(item.path);
226
+ merged.push(item);
227
+ }
228
+ return merged;
229
+ }
230
+ function isExcludedByReadMap(node, excludedPaths) {
231
+ return Boolean(node.path && excludedPaths.has(node.path));
232
+ }
150
233
  function normalizeContextBudget(input = {}) {
151
234
  const maxReadFirstItems = positiveInteger(input.maxReadFirstItems) ?? exports.CONTEXT_PACK_DEFAULT_BUDGET.maxReadFirstItems;
152
235
  return {
@@ -284,19 +367,24 @@ function itemFromRankedNode(ranked, required, projectRoot) {
284
367
  function sourceHashForItem(projectRoot, node) {
285
368
  if (!node.path || !(0, context_slice_boundary_1.isContextSliceProjectRelativePath)(node.path))
286
369
  return node.source.hash;
287
- const normalized = (0, context_slice_boundary_1.normalizeContextSliceInputPath)(node.path);
370
+ return sourceHashForPath(projectRoot, node.path) ?? node.source.hash;
371
+ }
372
+ function sourceHashForPath(projectRoot, relativePath) {
373
+ if (!(0, context_slice_boundary_1.isContextSliceProjectRelativePath)(relativePath))
374
+ return undefined;
375
+ const normalized = (0, context_slice_boundary_1.normalizeContextSliceInputPath)(relativePath);
288
376
  const root = node_path_1.default.resolve(projectRoot);
289
377
  const absolutePath = node_path_1.default.resolve(root, normalized);
290
378
  if (absolutePath !== root && !absolutePath.startsWith(`${root}${node_path_1.default.sep}`))
291
- return node.source.hash;
379
+ return undefined;
292
380
  try {
293
381
  const stat = node_fs_1.default.statSync(absolutePath);
294
382
  if (!stat.isFile())
295
- return node.source.hash;
383
+ return undefined;
296
384
  return (0, extractor_contract_1.hashContextGraphText)(node_fs_1.default.readFileSync(absolutePath, 'utf8'));
297
385
  }
298
386
  catch {
299
- return node.source.hash;
387
+ return undefined;
300
388
  }
301
389
  }
302
390
  function sourceAccessForNode(node) {
@@ -306,7 +394,10 @@ function sourceAccessForNode(node) {
306
394
  reason: 'This context item has no project file path for raw context slicing.'
307
395
  };
308
396
  }
309
- if ((0, context_slice_boundary_1.isContextSliceProjectRelativePath)(node.path)) {
397
+ return sourceAccessForPath(node.path);
398
+ }
399
+ function sourceAccessForPath(relativePath) {
400
+ if ((0, context_slice_boundary_1.isContextSliceProjectRelativePath)(relativePath)) {
310
401
  return {
311
402
  rawSlice: 'sliceable',
312
403
  reason: 'This item path is inside the raw context-slice read boundary.'
@@ -322,6 +413,9 @@ function estimateTokens(node) {
322
413
  const pathCost = node.path ? Math.ceil(node.path.length / 4) : 0;
323
414
  return Math.max(24, labelCost + pathCost + 16);
324
415
  }
416
+ function estimateTokensForPath(relativePath, title) {
417
+ return Math.max(24, Math.ceil(title.length / 4) + Math.ceil(relativePath.length / 4) + 16);
418
+ }
325
419
  function validationSuggestionsForTask(taskId, graphReport) {
326
420
  const suggestions = new Map();
327
421
  if (taskId) {
@@ -7,6 +7,8 @@ const context_graph_builder_1 = require("./context-graph-builder");
7
7
  const context_cache_store_1 = require("./context-cache-store");
8
8
  const source_manifest_1 = require("./source-manifest");
9
9
  const code_graph_extractor_1 = require("./code-graph-extractor");
10
+ const validate_1 = require("../harness/validate");
11
+ const docs_registry_1 = require("../services/docs-registry");
10
12
  exports.SESSION_START_SCHEMA_ID = 'hadara.sessionStart.v1';
11
13
  exports.SESSION_START_COMMAND = 'session.start';
12
14
  function buildSessionStartReport(input) {
@@ -41,6 +43,7 @@ function buildSessionStartReport(input) {
41
43
  lifecycle,
42
44
  allowLiveContextPack: Boolean(input.allowLiveContextPack)
43
45
  });
46
+ const docsReadMap = taskId ? createSessionStartDocsReadMap(input.projectRoot, taskId, input.budget?.maxReadFirstItems ?? 7) : undefined;
44
47
  return {
45
48
  schemaVersion: exports.SESSION_START_SCHEMA_ID,
46
49
  command: exports.SESSION_START_COMMAND,
@@ -56,6 +59,7 @@ function buildSessionStartReport(input) {
56
59
  contextPack,
57
60
  lifecycle,
58
61
  guidance,
62
+ ...(docsReadMap ? { docsReadMap } : {}),
59
63
  knownProblems: contextPack.knownProblems,
60
64
  sourceSummary: contextPack.sourceSummary,
61
65
  cache: contextPack.cache,
@@ -70,6 +74,43 @@ function buildSessionStartReport(input) {
70
74
  issues
71
75
  };
72
76
  }
77
+ function createSessionStartDocsReadMap(projectRoot, taskId, maxReadFirst) {
78
+ const readMap = (0, docs_registry_1.createDocsReadMapReport)(projectRoot, taskId);
79
+ const sourceValidation = (0, validate_1.validateTaskCapsule)(projectRoot, taskId, { level: 'done' });
80
+ const sourceDocumentDrift = sourceValidation.issues
81
+ .filter((issue) => issue.code === 'TASK_SOURCE_DOCUMENT_CHANGED' || issue.code === 'TASK_SOURCE_DOCUMENT_MISSING_HASH')
82
+ .map((issue) => ({
83
+ code: issue.code,
84
+ message: issue.message,
85
+ ...(issue.path ? { path: issue.path } : {}),
86
+ ...(issue.heading ? { heading: issue.heading } : {})
87
+ }));
88
+ return {
89
+ taskId: readMap.taskId,
90
+ command: `node dist/cli/main.js docs read-map --task ${taskId} --json`,
91
+ source: readMap.source,
92
+ task: readMap.task,
93
+ readFirstCount: readMap.readFirst.length,
94
+ readIfNeededCount: readMap.readIfNeeded.length,
95
+ doNotReadByDefaultCount: readMap.doNotReadByDefault.length,
96
+ driftWarningCount: readMap.driftWarnings.length,
97
+ sourceDocumentDriftCount: sourceDocumentDrift.length,
98
+ readFirst: readMap.readFirst.slice(0, maxReadFirst).map((entry) => ({
99
+ path: entry.path,
100
+ readTier: entry.readTier,
101
+ authority: entry.authority,
102
+ reason: entry.reason
103
+ })),
104
+ driftWarnings: readMap.driftWarnings.slice(0, 10),
105
+ sourceDocumentDrift,
106
+ issues: readMap.issues.map((issue) => ({
107
+ severity: issue.severity,
108
+ code: issue.code,
109
+ message: issue.message,
110
+ ...(issue.path ? { path: issue.path } : {})
111
+ }))
112
+ };
113
+ }
73
114
  function buildWarmCachedContextPackReport(input) {
74
115
  const cachedManifest = (0, context_cache_store_1.readContextSourceManifestCache)(input.projectRoot);
75
116
  if (cachedManifest.status !== 'valid' || !cachedManifest.manifest)
@@ -134,11 +175,10 @@ function buildWarmCachedContextPackReport(input) {
134
175
  function lifecycleForSessionStart(taskId, contextPack) {
135
176
  const primaryNextCommands = taskId
136
177
  ? [
137
- `node dist/cli/main.js task lifecycle --task ${taskId} --json`,
138
178
  `node dist/cli/main.js task status --task ${taskId} --json`,
139
179
  `node dist/cli/main.js context pack --task ${taskId} --json`
140
180
  ]
141
- : ['node dist/cli/main.js task next --json'];
181
+ : ['node dist/cli/main.js task status --json'];
142
182
  for (const suggestion of contextPack.validateWith) {
143
183
  if (suggestion.requiredForClose && !primaryNextCommands.includes(suggestion.command)) {
144
184
  primaryNextCommands.push(suggestion.command);
@@ -179,42 +219,36 @@ function guidanceForSessionStart(input) {
179
219
  let primaryAction;
180
220
  if (!taskId) {
181
221
  primaryAction = {
182
- id: 'task-next',
222
+ id: 'task-status',
183
223
  label: 'Select the next task',
184
- command: 'node dist/cli/main.js task next --json',
185
- args: ['task', 'next', '--json'],
224
+ command: 'node dist/cli/main.js task status --json',
225
+ args: ['task', 'status', '--json'],
186
226
  reason: 'No task id is available, so the next useful step is to select a concrete task before reading task-scoped context.',
187
227
  writeBoundary: 'read-only',
188
228
  recommendedActorRole: 'agent-worker'
189
229
  };
190
230
  commands.push({
191
- id: 'task-next',
192
- command: 'node dist/cli/main.js task next --json',
193
- args: ['task', 'next', '--json'],
231
+ id: 'task-status',
232
+ command: 'node dist/cli/main.js task status --json',
233
+ args: ['task', 'status', '--json'],
194
234
  reason: 'Choose the next task before requesting task-scoped context.'
195
235
  });
196
236
  }
197
237
  else {
198
238
  primaryAction = {
199
- id: 'task-lifecycle',
200
- label: 'Inspect task lifecycle phase',
201
- command: `node dist/cli/main.js task lifecycle --task ${taskId} --json`,
202
- args: ['task', 'lifecycle', '--task', taskId, '--json'],
203
- reason: 'A task id is available, so the fastest safe first step is to inspect lifecycle phase and blockers before editing files.',
239
+ id: 'task-status',
240
+ label: 'Inspect task loop phase',
241
+ command: `node dist/cli/main.js task status --task ${taskId} --json`,
242
+ args: ['task', 'status', '--task', taskId, '--json'],
243
+ reason: 'A task id is available, so the fastest safe first step is to inspect loop phase, blockers, and the primary next action before editing files.',
204
244
  writeBoundary: 'read-only',
205
245
  recommendedActorRole: 'agent-worker'
206
246
  };
207
- commands.push({
208
- id: 'task-lifecycle',
209
- command: `node dist/cli/main.js task lifecycle --task ${taskId} --json`,
210
- args: ['task', 'lifecycle', '--task', taskId, '--json'],
211
- reason: 'Inspect the task phase and the one primary lifecycle next action before editing.'
212
- });
213
247
  commands.push({
214
248
  id: 'task-status',
215
249
  command: `node dist/cli/main.js task status --task ${taskId} --json`,
216
250
  args: ['task', 'status', '--task', taskId, '--json'],
217
- reason: 'Inspect task readiness, evidence, and lifecycle state.'
251
+ reason: 'Inspect task readiness, evidence, loop phase, and the primary next action.'
218
252
  });
219
253
  commands.push({
220
254
  id: 'context-pack',
@@ -222,6 +256,12 @@ function guidanceForSessionStart(input) {
222
256
  args: ['context', 'pack', '--task', taskId, '--json'],
223
257
  reason: 'Inspect the bounded task read plan without slicing raw source text.'
224
258
  });
259
+ commands.push({
260
+ id: 'docs-read-map',
261
+ command: `node dist/cli/main.js docs read-map --task ${taskId} --json`,
262
+ args: ['docs', 'read-map', '--task', taskId, '--json'],
263
+ reason: 'Inspect registry-backed task reading guidance and drift warnings before broad manual reads.'
264
+ });
225
265
  }
226
266
  commands.push({
227
267
  id: 'cache-warm',
@@ -278,7 +318,7 @@ function buildBoundedContextPackReport(input) {
278
318
  severity: 'warning',
279
319
  code: 'CONTEXT_PACK_TASK_NOT_FOUND',
280
320
  message: 'No task id was supplied. Bounded session start returned task-selection guidance without running live project discovery.',
281
- fixHint: 'Run hadara task next --json, then rerun hadara session start --task <task-id> --json.'
321
+ fixHint: 'Run hadara task status --json, then rerun hadara session start --task <task-id> --json.'
282
322
  }];
283
323
  const readFirst = input.taskId
284
324
  ? [{
@@ -138,8 +138,9 @@ function readTaskStatus(content) {
138
138
  const sectionStatus = (0, markdown_table_1.readMarkdownSection)(content, '## Status').trim().split(/\r?\n/)[0]?.trim();
139
139
  if (sectionStatus)
140
140
  return sectionStatus;
141
- const rows = (0, markdown_table_1.parseMarkdownRowsUnderHeading)(content, '## Metadata');
142
- return (0, markdown_table_1.findMarkdownRowByCell)(rows, 0, 'Status')?.[1] ?? null;
141
+ const identityRows = (0, markdown_table_1.parseMarkdownRowsUnderHeading)(content, '## Identity');
142
+ const metadataRows = (0, markdown_table_1.parseMarkdownRowsUnderHeading)(content, '## Metadata');
143
+ return (0, markdown_table_1.findMarkdownRowByCell)(identityRows, 0, 'Status')?.[1] ?? (0, markdown_table_1.findMarkdownRowByCell)(metadataRows, 0, 'Status')?.[1] ?? null;
143
144
  }
144
145
  function readTaskHandoffStatus(content) {
145
146
  const rows = (0, markdown_table_1.parseMarkdownRowsUnderHeading)(content, '## Current State');
@@ -12,6 +12,7 @@ const actor_context_schema_json_1 = __importDefault(require("../schemas/actor-co
12
12
  const active_run_projection_schema_json_1 = __importDefault(require("../schemas/active-run-projection.schema.json"));
13
13
  const active_run_resume_schema_json_1 = __importDefault(require("../schemas/active-run-resume.schema.json"));
14
14
  const clean_checkout_smoke_schema_json_1 = __importDefault(require("../schemas/clean-checkout-smoke.schema.json"));
15
+ const close_source_schema_json_1 = __importDefault(require("../schemas/close-source.schema.json"));
15
16
  const code_index_schema_json_1 = __importDefault(require("../schemas/code-index.schema.json"));
16
17
  const context_cache_record_schema_json_1 = __importDefault(require("../schemas/context-cache-record.schema.json"));
17
18
  const context_cache_status_schema_json_1 = __importDefault(require("../schemas/context-cache-status.schema.json"));
@@ -29,13 +30,17 @@ const dev_docker_check_schema_json_1 = __importDefault(require("../schemas/dev-d
29
30
  const docs_archive_plan_schema_json_1 = __importDefault(require("../schemas/docs-archive-plan.schema.json"));
30
31
  const docs_doctor_schema_json_1 = __importDefault(require("../schemas/docs-doctor.schema.json"));
31
32
  const docs_explain_schema_json_1 = __importDefault(require("../schemas/docs-explain.schema.json"));
33
+ const docs_inbox_schema_json_1 = __importDefault(require("../schemas/docs-inbox.schema.json"));
32
34
  const docs_list_schema_json_1 = __importDefault(require("../schemas/docs-list.schema.json"));
33
35
  const docs_mark_schema_json_1 = __importDefault(require("../schemas/docs-mark.schema.json"));
34
36
  const docs_patch_plan_schema_json_1 = __importDefault(require("../schemas/docs-patch-plan.schema.json"));
37
+ const docs_read_map_schema_json_1 = __importDefault(require("../schemas/docs-read-map.schema.json"));
38
+ const docs_register_schema_json_1 = __importDefault(require("../schemas/docs-register.schema.json"));
35
39
  const docs_required_reading_schema_json_1 = __importDefault(require("../schemas/docs-required-reading.schema.json"));
36
40
  const evidence_lint_schema_json_1 = __importDefault(require("../schemas/evidence-lint.schema.json"));
37
41
  const evidence_list_schema_json_1 = __importDefault(require("../schemas/evidence-list.schema.json"));
38
42
  const evidence_migration_preview_schema_json_1 = __importDefault(require("../schemas/evidence-migration-preview.schema.json"));
43
+ const evidence_projection_schema_json_1 = __importDefault(require("../schemas/evidence-projection.schema.json"));
39
44
  const evidence_summary_schema_json_1 = __importDefault(require("../schemas/evidence-summary.schema.json"));
40
45
  const event_schema_json_1 = __importDefault(require("../schemas/event.schema.json"));
41
46
  const feature_smoke_schema_json_1 = __importDefault(require("../schemas/feature-smoke.schema.json"));
@@ -74,9 +79,11 @@ const task_finish_schema_json_1 = __importDefault(require("../schemas/task-finis
74
79
  const task_lifecycle_schema_json_1 = __importDefault(require("../schemas/task-lifecycle.schema.json"));
75
80
  const task_next_schema_json_1 = __importDefault(require("../schemas/task-next.schema.json"));
76
81
  const task_ready_schema_json_1 = __importDefault(require("../schemas/task-ready.schema.json"));
82
+ const task_status_schema_json_1 = __importDefault(require("../schemas/task-status.schema.json"));
77
83
  const task_upgrade_scaffold_schema_json_1 = __importDefault(require("../schemas/task-upgrade-scaffold.schema.json"));
78
84
  const task_workbench_schema_json_1 = __importDefault(require("../schemas/task-workbench.schema.json"));
79
85
  const tools_list_schema_json_1 = __importDefault(require("../schemas/tools-list.schema.json"));
86
+ const validation_run_schema_json_1 = __importDefault(require("../schemas/validation-run.schema.json"));
80
87
  const write_preflight_schema_json_1 = __importDefault(require("../schemas/write-preflight.schema.json"));
81
88
  class SchemaValidationError extends Error {
82
89
  schemaId;
@@ -96,6 +103,7 @@ const registeredSchemas = {
96
103
  'hadara.active_run.projection.v1': active_run_projection_schema_json_1.default,
97
104
  'hadara.active_run.resume.v1': active_run_resume_schema_json_1.default,
98
105
  'hadara.cleanCheckoutSmoke.v1': clean_checkout_smoke_schema_json_1.default,
106
+ 'hadara.closeSource.v1': close_source_schema_json_1.default,
99
107
  'hadara.codeIndex.v1': code_index_schema_json_1.default,
100
108
  'hadara.context.cacheRecord.v1': context_cache_record_schema_json_1.default,
101
109
  'hadara.context.cacheStatus.v1': context_cache_status_schema_json_1.default,
@@ -113,13 +121,17 @@ const registeredSchemas = {
113
121
  'hadara.docs.archivePlan.v1': docs_archive_plan_schema_json_1.default,
114
122
  'hadara.docs.doctor.v1': docs_doctor_schema_json_1.default,
115
123
  'hadara.docs.explain.v1': docs_explain_schema_json_1.default,
124
+ 'hadara.docs.inbox.v1': docs_inbox_schema_json_1.default,
116
125
  'hadara.docs.list.v1': docs_list_schema_json_1.default,
117
126
  'hadara.docs.mark.v1': docs_mark_schema_json_1.default,
118
127
  'hadara.docs.patchPlan.v1': docs_patch_plan_schema_json_1.default,
128
+ 'hadara.docs.readMap.v1': docs_read_map_schema_json_1.default,
129
+ 'hadara.docs.register.v1': docs_register_schema_json_1.default,
119
130
  'hadara.docs.requiredReading.v1': docs_required_reading_schema_json_1.default,
120
131
  'hadara.evidence.lint.v1': evidence_lint_schema_json_1.default,
121
132
  'hadara.evidence.list.v1': evidence_list_schema_json_1.default,
122
133
  'hadara.evidence.migration_preview.v1': evidence_migration_preview_schema_json_1.default,
134
+ 'hadara.evidence.projection.v1': evidence_projection_schema_json_1.default,
123
135
  'hadara.evidence.summary.v1': evidence_summary_schema_json_1.default,
124
136
  'hadara.event.v1': event_schema_json_1.default,
125
137
  'hadara.featureSmoke.v1': feature_smoke_schema_json_1.default,
@@ -158,9 +170,11 @@ const registeredSchemas = {
158
170
  'hadara.task.lifecycle.v1': task_lifecycle_schema_json_1.default,
159
171
  'hadara.task.next.v1': task_next_schema_json_1.default,
160
172
  'hadara.task.ready.v1': task_ready_schema_json_1.default,
173
+ 'hadara.task.status.v1': task_status_schema_json_1.default,
161
174
  'hadara.task.upgrade_scaffold.v1': task_upgrade_scaffold_schema_json_1.default,
162
175
  'hadara.task.workbench.v1': task_workbench_schema_json_1.default,
163
176
  'hadara.tools.list.v1': tools_list_schema_json_1.default,
177
+ 'hadara.validation.run.v1': validation_run_schema_json_1.default,
164
178
  'hadara.write.preflight.v1': write_preflight_schema_json_1.default
165
179
  };
166
180
  function loadSchema(schemaId) {
@@ -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
+ }
@@ -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 started = Date.now();
51
+ const timer = (0, timing_1.startMonotonicTimer)();
51
52
  const result = runner.run('docker', dockerExecArgs(container, step.script), env);
52
- const elapsedMs = Date.now() - started;
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)