hadara 0.1.0-rc.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 (121) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +109 -0
  3. package/dist/agent/evidence.js +50 -0
  4. package/dist/agent/loop.js +124 -0
  5. package/dist/cli/args.js +70 -0
  6. package/dist/cli/dashboard.js +185 -0
  7. package/dist/cli/debt.js +41 -0
  8. package/dist/cli/doctor.js +68 -0
  9. package/dist/cli/errors.js +58 -0
  10. package/dist/cli/evidence-json.js +75 -0
  11. package/dist/cli/evidence.js +80 -0
  12. package/dist/cli/handoff.js +16 -0
  13. package/dist/cli/harness.js +57 -0
  14. package/dist/cli/hermes-json.js +31 -0
  15. package/dist/cli/hermes.js +28 -0
  16. package/dist/cli/init.js +142 -0
  17. package/dist/cli/install.js +34 -0
  18. package/dist/cli/main.js +216 -0
  19. package/dist/cli/mcp.js +15 -0
  20. package/dist/cli/package-smoke.js +37 -0
  21. package/dist/cli/policy-json.js +22 -0
  22. package/dist/cli/policy.js +43 -0
  23. package/dist/cli/release-artifact.js +47 -0
  24. package/dist/cli/release-dry-run.js +24 -0
  25. package/dist/cli/release-gate.js +28 -0
  26. package/dist/cli/release-publish.js +41 -0
  27. package/dist/cli/run-scaffold.js +68 -0
  28. package/dist/cli/run-state.js +41 -0
  29. package/dist/cli/run.js +191 -0
  30. package/dist/cli/smoke.js +58 -0
  31. package/dist/cli/status-json.js +6 -0
  32. package/dist/cli/status.js +26 -0
  33. package/dist/cli/task-json.js +8 -0
  34. package/dist/cli/task.js +64 -0
  35. package/dist/cli/tools.js +25 -0
  36. package/dist/cli/tui.js +72 -0
  37. package/dist/cli/write-preflight.js +27 -0
  38. package/dist/core/audit.js +41 -0
  39. package/dist/core/events.js +63 -0
  40. package/dist/core/fs.js +44 -0
  41. package/dist/core/paths.js +59 -0
  42. package/dist/core/redaction.js +178 -0
  43. package/dist/core/schema.js +253 -0
  44. package/dist/core/workspace.js +47 -0
  45. package/dist/evidence/evidence.js +170 -0
  46. package/dist/evidence/private-manifest.js +101 -0
  47. package/dist/handoff/handoff.js +49 -0
  48. package/dist/harness/replay.js +200 -0
  49. package/dist/harness/validate.js +465 -0
  50. package/dist/hermes/context-export.js +104 -0
  51. package/dist/index.js +29 -0
  52. package/dist/mcp/server.js +104 -0
  53. package/dist/mcp/tool-dispatch.js +159 -0
  54. package/dist/mcp/tool-registry.js +150 -0
  55. package/dist/mcp/tool-schemas.js +18 -0
  56. package/dist/policy/command-risk.js +39 -0
  57. package/dist/policy/permission-matrix.js +42 -0
  58. package/dist/policy/policy.js +20 -0
  59. package/dist/policy/preflight.js +47 -0
  60. package/dist/policy/presets.js +24 -0
  61. package/dist/policy/tokenizer.js +53 -0
  62. package/dist/providers/fallback-executor.js +46 -0
  63. package/dist/providers/mock-provider.js +49 -0
  64. package/dist/providers/provider-contract.js +2 -0
  65. package/dist/providers/provider-preparation.js +220 -0
  66. package/dist/providers/scripted-provider.js +69 -0
  67. package/dist/schemas/active-run-projection.schema.json +73 -0
  68. package/dist/schemas/active-run-resume.schema.json +68 -0
  69. package/dist/schemas/clean-checkout-smoke.schema.json +126 -0
  70. package/dist/schemas/context-export.schema.json +35 -0
  71. package/dist/schemas/event.schema.json +17 -0
  72. package/dist/schemas/evidence-list.schema.json +49 -0
  73. package/dist/schemas/feature-smoke.schema.json +67 -0
  74. package/dist/schemas/install-plan.schema.json +93 -0
  75. package/dist/schemas/package-smoke.schema.json +130 -0
  76. package/dist/schemas/private-evidence.schema.json +48 -0
  77. package/dist/schemas/provider-call.schema.json +42 -0
  78. package/dist/schemas/provider-config.schema.json +43 -0
  79. package/dist/schemas/release-artifact-manifest.schema.json +55 -0
  80. package/dist/schemas/release-artifact.schema.json +140 -0
  81. package/dist/schemas/release-dry-run.schema.json +141 -0
  82. package/dist/schemas/release-gate.schema.json +42 -0
  83. package/dist/schemas/release-publish.schema.json +114 -0
  84. package/dist/schemas/schema-index.json +145 -0
  85. package/dist/schemas/smoke-evidence-summary.schema.json +88 -0
  86. package/dist/schemas/tools-list.schema.json +78 -0
  87. package/dist/schemas/write-preflight.schema.json +47 -0
  88. package/dist/services/active-run-state.js +215 -0
  89. package/dist/services/capability-registry.js +540 -0
  90. package/dist/services/clean-checkout-smoke.js +393 -0
  91. package/dist/services/evidence-list.js +136 -0
  92. package/dist/services/feature-smoke.js +155 -0
  93. package/dist/services/harness-service.js +7 -0
  94. package/dist/services/install-plan.js +233 -0
  95. package/dist/services/operational-debt.js +767 -0
  96. package/dist/services/operations-status-service.js +195 -0
  97. package/dist/services/package-smoke.js +676 -0
  98. package/dist/services/policy-service.js +25 -0
  99. package/dist/services/project-read-model.js +101 -0
  100. package/dist/services/release-artifact-evidence.js +77 -0
  101. package/dist/services/release-artifact.js +351 -0
  102. package/dist/services/release-dry-run.js +253 -0
  103. package/dist/services/release-evidence.js +138 -0
  104. package/dist/services/release-publish.js +163 -0
  105. package/dist/services/smoke-evidence.js +104 -0
  106. package/dist/services/task-read-model.js +125 -0
  107. package/dist/services/tools-list.js +26 -0
  108. package/dist/services/write-preflight.js +240 -0
  109. package/dist/task/task-capsule.js +121 -0
  110. package/dist/tools/fake-shell.js +56 -0
  111. package/dist/tui/cache.js +341 -0
  112. package/dist/tui/constants.js +44 -0
  113. package/dist/tui/layout.js +140 -0
  114. package/dist/tui/markdown.js +238 -0
  115. package/dist/tui/read-model-worker.js +24 -0
  116. package/dist/tui/read-model.js +502 -0
  117. package/dist/tui/snapshot.js +434 -0
  118. package/dist/tui/state.js +229 -0
  119. package/dist/tui/terminal.js +475 -0
  120. package/dist/tui/theme.js +86 -0
  121. package/package.json +16 -0
@@ -0,0 +1,502 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.createTuiLoadingReadModel = createTuiLoadingReadModel;
7
+ exports.createTuiReadModel = createTuiReadModel;
8
+ exports.createTuiFastReadModel = createTuiFastReadModel;
9
+ const active_run_state_1 = require("../services/active-run-state");
10
+ const active_run_state_2 = require("../services/active-run-state");
11
+ const evidence_list_1 = require("../services/evidence-list");
12
+ const operational_debt_1 = require("../services/operational-debt");
13
+ const operations_status_service_1 = require("../services/operations-status-service");
14
+ const project_read_model_1 = require("../services/project-read-model");
15
+ const task_read_model_1 = require("../services/task-read-model");
16
+ const tools_list_1 = require("../services/tools-list");
17
+ const write_preflight_1 = require("../services/write-preflight");
18
+ const node_fs_1 = __importDefault(require("node:fs"));
19
+ const node_path_1 = __importDefault(require("node:path"));
20
+ const DEFAULT_EVIDENCE_LIMIT = 20;
21
+ const DEFAULT_WRITE_PREVIEW_TITLE = 'TUI Follow-up';
22
+ function createTuiLoadingReadModel() {
23
+ const generatedAt = new Date().toISOString();
24
+ return {
25
+ schemaVersion: 'hadara.tui.read_model.internal.v1',
26
+ command: 'tui.read-model',
27
+ ok: true,
28
+ generatedAt,
29
+ selectedTaskId: null,
30
+ overview: {
31
+ currentWork: null,
32
+ previousWork: null,
33
+ currentDetail: null,
34
+ previousDetail: null,
35
+ health: 'loading',
36
+ phase: 'loading read models',
37
+ branch: '...'
38
+ },
39
+ status: {
40
+ schemaVersion: 'hadara.ops.status.v1',
41
+ command: 'ops.status',
42
+ ok: true,
43
+ health: 'loading',
44
+ project: { branch: '...', phase: 'loading read models' },
45
+ tasks: {
46
+ counts: { done: 0, draft: 0, partial: 0, superseded: 0, inProgress: 0, unknown: 0 },
47
+ rawStatusCounts: {},
48
+ normalizedStatusCounts: {},
49
+ lastCompleted: [],
50
+ nextRecommended: 'Reading HADARA project state...'
51
+ },
52
+ handoff: { currentState: [], knownProblems: [], nextRecommendedStep: [] },
53
+ validation: { latestFullCheck: 'loading', latestDoneLevelValidation: null },
54
+ activeRun: {
55
+ schemaVersion: 'hadara.active_run.projection.v1',
56
+ command: 'active-run.projection',
57
+ ok: true,
58
+ path: '.hadara/local/state/active-run.json',
59
+ activeRun: null,
60
+ handoff: { fresh: true, staleReason: null },
61
+ resume: null,
62
+ issues: []
63
+ },
64
+ debt: { total: 0, open: 0, tracked: 0, mitigated: 0, candidate: 0, highOpen: 0, bySeverity: {} },
65
+ mcp: {
66
+ defaultMode: 'read-only',
67
+ evidenceAttach: { enabledByDefault: false, requiresFlag: '--enable-evidence-attach', requiresApproval: true, audited: true }
68
+ },
69
+ issues: []
70
+ },
71
+ tasks: {
72
+ schemaVersion: 'hadara.task.list.v1',
73
+ command: 'task.list',
74
+ ok: true,
75
+ count: 0,
76
+ tasks: [],
77
+ issues: []
78
+ },
79
+ selectedTask: null,
80
+ activeRun: {
81
+ projection: {
82
+ schemaVersion: 'hadara.active_run.projection.v1',
83
+ command: 'active-run.projection',
84
+ ok: true,
85
+ path: '.hadara/local/state/active-run.json',
86
+ activeRun: null,
87
+ handoff: { fresh: true, staleReason: null },
88
+ resume: null,
89
+ issues: []
90
+ },
91
+ resume: {
92
+ schemaVersion: 'hadara.active_run.resume.v1',
93
+ command: 'active-run.resume',
94
+ ok: true,
95
+ activeRun: null,
96
+ resumePrompt: {
97
+ summary: 'Reading active-run state...',
98
+ mustRead: [],
99
+ nextActions: [],
100
+ constraints: ['Read-only mode remains enforced.']
101
+ },
102
+ issues: []
103
+ }
104
+ },
105
+ debt: {
106
+ schemaVersion: 'hadara.operational_debt.list.v1',
107
+ command: 'debt.list',
108
+ ok: true,
109
+ aggregate: { total: 0, open: 0, tracked: 0, mitigated: 0, candidate: 0, highOpen: 0, bySeverity: {} },
110
+ records: [],
111
+ debts: [],
112
+ capsuleSizeIndicators: [],
113
+ issues: []
114
+ },
115
+ releaseGate: {
116
+ schemaVersion: 'hadara.releaseGate.v1',
117
+ command: 'release.gate',
118
+ ok: true,
119
+ mode: 'advisory',
120
+ checks: [],
121
+ issues: []
122
+ },
123
+ tools: {
124
+ schemaVersion: 'hadara.tools.list.v1',
125
+ command: 'tools.list',
126
+ ok: true,
127
+ tools: [],
128
+ surfaces: [],
129
+ disabled: [],
130
+ issues: []
131
+ },
132
+ writePreview: {
133
+ schemaVersion: 'hadara.write.preflight.v1',
134
+ command: 'unknown',
135
+ ok: true,
136
+ writes: [],
137
+ risk: 'low',
138
+ requiresApproval: false,
139
+ workspaceBoundary: 'project',
140
+ issues: []
141
+ },
142
+ issues: []
143
+ };
144
+ }
145
+ function createTuiReadModel(projectRoot, options = {}) {
146
+ if (options.profile === 'fast')
147
+ return createTuiFastReadModel(projectRoot, options);
148
+ const status = (0, operations_status_service_1.createOpsStatusReport)(projectRoot);
149
+ const tasks = (0, task_read_model_1.createTaskListReport)(projectRoot);
150
+ const selectedTaskId = resolveSelectedTaskId(tasks.tasks, status, options.selectedTaskId);
151
+ const selectedSummary = selectedTaskId ? tasks.tasks.find((task) => task.id === selectedTaskId) ?? null : null;
152
+ const selectedTask = selectedSummary
153
+ ? {
154
+ summary: selectedSummary,
155
+ detail: (0, task_read_model_1.createTaskReadReport)(projectRoot, selectedSummary.id, { includePrivate: options.includePrivateEvidence }),
156
+ evidence: (0, evidence_list_1.createEvidenceListReport)(projectRoot, {
157
+ taskId: selectedSummary.id,
158
+ limit: options.evidenceLimit ?? DEFAULT_EVIDENCE_LIMIT,
159
+ includePrivate: options.includePrivateEvidence
160
+ })
161
+ }
162
+ : null;
163
+ const activeRunResume = (0, active_run_state_1.createActiveRunResumeReport)(projectRoot);
164
+ const debt = (0, operational_debt_1.createOperationalDebtReport)(projectRoot);
165
+ const releaseGate = (0, operational_debt_1.createReleaseGateReport)(projectRoot, 'advisory');
166
+ const tools = (0, tools_list_1.createToolsListReport)();
167
+ const writePreview = (0, write_preflight_1.createWritePreflightReport)(projectRoot, ['task', 'create', options.writePreviewTitle ?? DEFAULT_WRITE_PREVIEW_TITLE]);
168
+ const overview = createOverview(projectRoot, tasks.tasks, selectedTask, status, options.includePrivateEvidence);
169
+ const issues = collectIssues({
170
+ status,
171
+ activeRunResume,
172
+ selectedTask,
173
+ releaseGate,
174
+ writePreview,
175
+ selectedTaskId,
176
+ explicitSelectedTaskId: options.selectedTaskId ?? null
177
+ });
178
+ return {
179
+ schemaVersion: 'hadara.tui.read_model.internal.v1',
180
+ command: 'tui.read-model',
181
+ ok: !issues.some((issue) => issue.severity === 'error'),
182
+ generatedAt: new Date().toISOString(),
183
+ selectedTaskId,
184
+ overview,
185
+ status,
186
+ tasks,
187
+ selectedTask,
188
+ activeRun: {
189
+ projection: status.activeRun,
190
+ resume: activeRunResume
191
+ },
192
+ debt,
193
+ releaseGate,
194
+ tools,
195
+ writePreview,
196
+ issues
197
+ };
198
+ }
199
+ function createTuiFastReadModel(projectRoot, options = {}) {
200
+ const sources = (0, project_read_model_1.readProjectSources)(projectRoot);
201
+ const tasks = (0, task_read_model_1.createTaskListReport)(projectRoot);
202
+ const activeRunProjection = (0, active_run_state_2.safeCreateActiveRunProjection)(projectRoot);
203
+ const status = createFastOpsStatusReport(projectRoot, sources, tasks, activeRunProjection);
204
+ const selectedTaskId = resolveSelectedTaskId(tasks.tasks, status, options.selectedTaskId);
205
+ const selectedSummary = selectedTaskId ? tasks.tasks.find((task) => task.id === selectedTaskId) ?? null : null;
206
+ const selectedTask = selectedSummary
207
+ ? {
208
+ summary: selectedSummary,
209
+ detail: (0, task_read_model_1.createTaskReadReport)(projectRoot, selectedSummary.id, { includePrivate: options.includePrivateEvidence }),
210
+ evidence: (0, evidence_list_1.createEvidenceListReport)(projectRoot, {
211
+ taskId: selectedSummary.id,
212
+ limit: options.evidenceLimit ?? DEFAULT_EVIDENCE_LIMIT,
213
+ includePrivate: options.includePrivateEvidence
214
+ })
215
+ }
216
+ : null;
217
+ const activeRunResume = (0, active_run_state_1.createActiveRunResumeReport)(projectRoot);
218
+ const deferredIssue = {
219
+ source: 'tui-read-model',
220
+ severity: 'warning',
221
+ code: 'TUI_HEAVY_READS_DEFERRED',
222
+ message: 'TUI fast read model deferred debt, release-gate, tools, and write-preflight reads.'
223
+ };
224
+ const issues = [
225
+ ...collectIssues({
226
+ status,
227
+ activeRunResume,
228
+ selectedTask,
229
+ releaseGate: createDeferredReleaseGateReport(),
230
+ writePreview: createDeferredWritePreflightReport(),
231
+ selectedTaskId,
232
+ explicitSelectedTaskId: options.selectedTaskId ?? null
233
+ }),
234
+ deferredIssue
235
+ ];
236
+ return {
237
+ schemaVersion: 'hadara.tui.read_model.internal.v1',
238
+ command: 'tui.read-model',
239
+ ok: !issues.some((issue) => issue.severity === 'error'),
240
+ generatedAt: new Date().toISOString(),
241
+ selectedTaskId,
242
+ overview: createOverview(projectRoot, tasks.tasks, selectedTask, status, options.includePrivateEvidence),
243
+ status,
244
+ tasks,
245
+ selectedTask,
246
+ activeRun: {
247
+ projection: status.activeRun,
248
+ resume: activeRunResume
249
+ },
250
+ debt: createDeferredDebtReport(),
251
+ releaseGate: createDeferredReleaseGateReport(),
252
+ tools: createDeferredToolsListReport(),
253
+ writePreview: createDeferredWritePreflightReport(),
254
+ issues
255
+ };
256
+ }
257
+ function createFastOpsStatusReport(projectRoot, sources, tasks, activeRun) {
258
+ const counts = countTaskStatuses(tasks.tasks);
259
+ const validation = {
260
+ latestFullCheck: extractValidationLine(sources.handoff.content, 'Latest full check') ?? extractValidationHistoryLine(sources.validationHistory.content, 'Docker check'),
261
+ latestDoneLevelValidation: extractValidationLine(sources.handoff.content, 'Latest done-level validation') ??
262
+ extractValidationHistoryLine(sources.validationHistory.content, 'harness validate')
263
+ };
264
+ const issues = [];
265
+ if (!sources.projectState.exists)
266
+ issues.push({ severity: 'warning', code: 'PROJECT_STATE_MISSING', message: 'docs/PROJECT_STATE.md is missing.' });
267
+ if (!sources.handoff.exists)
268
+ issues.push({ severity: 'warning', code: 'AGENT_HANDOFF_MISSING', message: 'docs/AGENT_HANDOFF.md is missing.' });
269
+ if (!sources.taskBoard.exists)
270
+ issues.push({ severity: 'warning', code: 'TASK_BOARD_MISSING', message: 'docs/TASK_BOARD.md is missing.' });
271
+ if (!sources.developmentSlices.exists)
272
+ issues.push({ severity: 'warning', code: 'DEVELOPMENT_SLICES_MISSING', message: 'docs/DEVELOPMENT_SLICES.md is missing.' });
273
+ if (!validation.latestFullCheck && !validation.latestDoneLevelValidation) {
274
+ issues.push({ severity: 'warning', code: 'VALIDATION_BASELINE_MISSING', message: 'No latest validation baseline was found in handoff or validation history.' });
275
+ }
276
+ issues.push(...activeRun.issues);
277
+ return {
278
+ schemaVersion: 'hadara.ops.status.v1',
279
+ command: 'ops.status',
280
+ ok: true,
281
+ health: issues.some((issue) => issue.severity === 'error') ? 'error' : issues.length > 0 ? 'degraded' : 'ok',
282
+ project: {
283
+ branch: readGitBranch(projectRoot),
284
+ phase: extractProjectPhase(sources.projectState.content)
285
+ },
286
+ tasks: {
287
+ counts: counts.counts,
288
+ rawStatusCounts: counts.rawStatusCounts,
289
+ normalizedStatusCounts: counts.normalizedStatusCounts,
290
+ lastCompleted: extractLastCompletedTaskIds(sources.handoff.content),
291
+ nextRecommended: extractListSection(sources.handoff.content, '## Next Recommended Step')[0] ?? null
292
+ },
293
+ handoff: {
294
+ currentState: extractListSection(sources.handoff.content, '## Current State'),
295
+ knownProblems: extractListSection(sources.handoff.content, '## Current Known Problems'),
296
+ nextRecommendedStep: extractListSection(sources.handoff.content, '## Next Recommended Step')
297
+ },
298
+ validation,
299
+ activeRun,
300
+ debt: { total: 0, open: 0, tracked: 0, mitigated: 0, candidate: 0, highOpen: 0, bySeverity: { high: 0, medium: 0, low: 0 } },
301
+ mcp: {
302
+ defaultMode: 'read-only',
303
+ evidenceAttach: {
304
+ enabledByDefault: false,
305
+ requiresFlag: '--enable-evidence-attach',
306
+ requiresApproval: true,
307
+ audited: true
308
+ }
309
+ },
310
+ issues
311
+ };
312
+ }
313
+ function resolveSelectedTaskId(tasks, status, explicitSelectedTaskId) {
314
+ if (explicitSelectedTaskId)
315
+ return explicitSelectedTaskId;
316
+ const activeTaskId = status.activeRun.activeRun?.taskId;
317
+ if (activeTaskId && tasks.some((task) => task.id === activeTaskId))
318
+ return activeTaskId;
319
+ return tasks.at(-1)?.id ?? null;
320
+ }
321
+ function createOverview(projectRoot, tasks, selectedTask, status, includePrivateEvidence) {
322
+ const latestRows = [...tasks].reverse();
323
+ const currentWork = latestRows[0] ?? null;
324
+ const previousWork = latestRows[1] ?? null;
325
+ const readDetail = (task) => {
326
+ if (!task)
327
+ return null;
328
+ if (selectedTask?.summary.id === task.id)
329
+ return selectedTask.detail;
330
+ return (0, task_read_model_1.createTaskReadReport)(projectRoot, task.id, { includePrivate: includePrivateEvidence });
331
+ };
332
+ return {
333
+ currentWork,
334
+ previousWork,
335
+ currentDetail: readDetail(currentWork),
336
+ previousDetail: readDetail(previousWork),
337
+ health: status.health,
338
+ phase: status.project.phase,
339
+ branch: status.project.branch
340
+ };
341
+ }
342
+ function collectIssues(input) {
343
+ const issues = [
344
+ ...input.status.issues.map((issue) => ({ source: 'status', ...issue })),
345
+ ...input.activeRunResume.issues.map((issue) => ({ source: 'active-run-resume', ...issue })),
346
+ ...input.releaseGate.issues.map((issue) => ({ source: 'release-gate', ...issue })),
347
+ ...input.writePreview.issues.map((issue) => ({ source: 'write-preflight', ...issue }))
348
+ ];
349
+ if (input.selectedTask) {
350
+ issues.push(...input.selectedTask.detail.issues.map((issue) => ({ source: 'task-detail', ...issue })));
351
+ issues.push(...input.selectedTask.evidence.issues.map((issue) => ({ source: 'evidence', ...issue })));
352
+ }
353
+ else if (input.explicitSelectedTaskId || input.selectedTaskId) {
354
+ const taskId = input.explicitSelectedTaskId ?? input.selectedTaskId ?? '(unknown)';
355
+ issues.push({
356
+ source: 'tui-read-model',
357
+ severity: 'error',
358
+ code: 'TUI_SELECTED_TASK_NOT_FOUND',
359
+ message: `Selected Task Capsule not found: ${taskId}`
360
+ });
361
+ }
362
+ return issues;
363
+ }
364
+ function countTaskStatuses(tasks) {
365
+ const counts = {
366
+ done: 0,
367
+ draft: 0,
368
+ partial: 0,
369
+ superseded: 0,
370
+ inProgress: 0,
371
+ unknown: 0
372
+ };
373
+ const rawStatusCounts = {};
374
+ const normalizedStatusCounts = {};
375
+ for (const task of tasks) {
376
+ const rawStatus = task.status || 'Unknown';
377
+ const normalizedStatus = normalizeStatus(rawStatus);
378
+ const aggregate = aggregateStatus(normalizedStatus);
379
+ counts[aggregate] += 1;
380
+ rawStatusCounts[rawStatus] = (rawStatusCounts[rawStatus] ?? 0) + 1;
381
+ normalizedStatusCounts[normalizedStatus] = (normalizedStatusCounts[normalizedStatus] ?? 0) + 1;
382
+ }
383
+ return { counts, rawStatusCounts, normalizedStatusCounts };
384
+ }
385
+ function readGitBranch(projectRoot) {
386
+ const headPath = node_path_1.default.join(projectRoot, '.git', 'HEAD');
387
+ if (!node_fs_1.default.existsSync(headPath))
388
+ return 'unknown';
389
+ const head = node_fs_1.default.readFileSync(headPath, 'utf8').trim();
390
+ const refPrefix = 'ref: refs/heads/';
391
+ if (head.startsWith(refPrefix))
392
+ return head.slice(refPrefix.length);
393
+ return head.length > 0 ? 'detached' : 'unknown';
394
+ }
395
+ function extractProjectPhase(projectState) {
396
+ const section = (0, project_read_model_1.extractSection)(projectState, '## Current Phase');
397
+ const line = section
398
+ .split(/\r?\n/)
399
+ .map((value) => value.trim())
400
+ .find(Boolean);
401
+ if (!line)
402
+ return 'unknown';
403
+ const explicit = line.match(/^Phase:\s*(.+)$/i);
404
+ if (explicit)
405
+ return explicit[1].trim();
406
+ if (/Phase 0\s*\/\s*Phase 1 boundary/i.test(line))
407
+ return 'bootstrap-development';
408
+ return line;
409
+ }
410
+ function extractListSection(content, heading) {
411
+ const section = (0, project_read_model_1.extractSection)(content, heading);
412
+ return section
413
+ .split(/\r?\n/)
414
+ .map((line) => line.trim().replace(/^[-*]\s+/, ''))
415
+ .filter(Boolean);
416
+ }
417
+ function extractLastCompletedTaskIds(handoff) {
418
+ return extractListSection(handoff, '## Last 3 Completed Tasks')
419
+ .map((line) => line.match(/^(T-\d{4})\b/)?.[1])
420
+ .filter((value) => Boolean(value));
421
+ }
422
+ function extractValidationLine(handoff, label) {
423
+ const validation = extractListSection(handoff, '## Validation Baseline');
424
+ const prefix = `${label}:`;
425
+ const line = validation.find((item) => item.startsWith(prefix));
426
+ return line ? line.slice(prefix.length).trim().replace(/\.$/, '') : null;
427
+ }
428
+ function extractValidationHistoryLine(validationHistory, pattern) {
429
+ const lines = validationHistory
430
+ .split(/\r?\n/)
431
+ .map((line) => line.trim().replace(/^[-*]\s+/, ''))
432
+ .filter((line) => line.includes(pattern));
433
+ return lines.at(-1)?.replace(/\.$/, '') ?? null;
434
+ }
435
+ function normalizeStatus(status) {
436
+ const value = status.trim().toLowerCase().replace(/[\s_-]+(.)/g, (_match, letter) => letter.toUpperCase());
437
+ return value || 'unknown';
438
+ }
439
+ function aggregateStatus(status) {
440
+ if (status === 'done')
441
+ return 'done';
442
+ if (status === 'draft')
443
+ return 'draft';
444
+ if (status === 'partial')
445
+ return 'partial';
446
+ if (status === 'superseded')
447
+ return 'superseded';
448
+ if (status === 'inProgress' || status === 'active' || status === 'doing')
449
+ return 'inProgress';
450
+ return 'unknown';
451
+ }
452
+ function createDeferredDebtReport() {
453
+ return {
454
+ schemaVersion: 'hadara.operational_debt.v1',
455
+ command: 'operational-debt.report',
456
+ ok: true,
457
+ records: [],
458
+ aggregate: { total: 0, open: 0, tracked: 0, mitigated: 0, candidate: 0, highOpen: 0, bySeverity: { high: 0, medium: 0, low: 0 } },
459
+ capsuleSizeIndicators: [],
460
+ issues: []
461
+ };
462
+ }
463
+ function createDeferredReleaseGateReport() {
464
+ return {
465
+ schemaVersion: 'hadara.releaseGate.v1',
466
+ command: 'release.gate',
467
+ mode: 'advisory',
468
+ ok: true,
469
+ checks: [
470
+ {
471
+ code: 'TUI_FAST_RELEASE_GATE_DEFERRED',
472
+ name: 'Deferred release-gate check',
473
+ status: 'warning',
474
+ summary: 'Release-gate debt scan is deferred in the TUI fast read model.'
475
+ }
476
+ ],
477
+ issues: []
478
+ };
479
+ }
480
+ function createDeferredToolsListReport() {
481
+ return {
482
+ schemaVersion: 'hadara.tools.list.v1',
483
+ command: 'tools.list',
484
+ ok: true,
485
+ tools: [],
486
+ surfaces: [],
487
+ disabled: [],
488
+ issues: []
489
+ };
490
+ }
491
+ function createDeferredWritePreflightReport() {
492
+ return {
493
+ schemaVersion: 'hadara.write.preflight.v1',
494
+ command: 'unknown',
495
+ ok: true,
496
+ writes: [],
497
+ risk: 'low',
498
+ requiresApproval: false,
499
+ workspaceBoundary: 'project',
500
+ issues: []
501
+ };
502
+ }