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,767 @@
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.OPERATIONAL_DEBT_RECORDS = void 0;
7
+ exports.createOperationalDebtReport = createOperationalDebtReport;
8
+ exports.createOperationalDebtShowReport = createOperationalDebtShowReport;
9
+ exports.createReleaseGateReport = createReleaseGateReport;
10
+ exports.createOperationalDebtAggregate = createOperationalDebtAggregate;
11
+ const node_fs_1 = __importDefault(require("node:fs"));
12
+ const node_path_1 = __importDefault(require("node:path"));
13
+ const schema_1 = require("../core/schema");
14
+ const task_capsule_1 = require("../task/task-capsule");
15
+ exports.OPERATIONAL_DEBT_RECORDS = [
16
+ {
17
+ id: 'OD-0001',
18
+ title: 'Task Capsule Markdown consistency can drift after context compaction',
19
+ source: 'known_issue.log#1',
20
+ category: 'validation',
21
+ status: 'mitigated',
22
+ severity: 'medium',
23
+ targetCapability: 'Task Capsule format validation'
24
+ },
25
+ {
26
+ id: 'OD-0002',
27
+ title: 'New sessions may miss Docker-based validation environment details',
28
+ source: 'known_issue.log#2',
29
+ category: 'environment',
30
+ status: 'mitigated',
31
+ severity: 'medium',
32
+ targetCapability: 'Validation environment handoff'
33
+ },
34
+ {
35
+ id: 'OD-0003',
36
+ title: 'Agents can overfit to the last capsule and miss broader roadmap state',
37
+ source: 'known_issue.log#3',
38
+ category: 'continuity',
39
+ status: 'mitigated',
40
+ severity: 'high',
41
+ targetCapability: 'Required-reading protocol and roadmap-aware handoff guidance'
42
+ },
43
+ {
44
+ id: 'OD-0004',
45
+ title: 'Long-running capsule work can concentrate too many features in one file',
46
+ source: 'known_issue.log#4',
47
+ category: 'complexity',
48
+ status: 'tracked',
49
+ severity: 'medium',
50
+ targetCapability: 'LOC and complexity risk indicators'
51
+ },
52
+ {
53
+ id: 'OD-0005',
54
+ title: 'LOC calculation utility is needed for complexity management',
55
+ source: 'known_issue.log#5',
56
+ category: 'complexity',
57
+ status: 'candidate',
58
+ severity: 'low',
59
+ targetCapability: 'Changed LOC utility'
60
+ },
61
+ {
62
+ id: 'OD-0006',
63
+ title: 'Capsule size should scale with task complexity',
64
+ source: 'known_issue.log#6',
65
+ category: 'scope-control',
66
+ status: 'tracked',
67
+ severity: 'medium',
68
+ targetCapability: 'Capsule size indicator'
69
+ },
70
+ {
71
+ id: 'OD-0007',
72
+ title: 'Task change size should be visible in dashboard or TUI surfaces',
73
+ source: 'known_issue.log#7',
74
+ category: 'visibility',
75
+ status: 'candidate',
76
+ severity: 'low',
77
+ targetCapability: 'Changed-size dashboard signal'
78
+ },
79
+ {
80
+ id: 'OD-0008',
81
+ title: 'ACCEPTANCE.md checkboxes can be marked before implementation evidence exists',
82
+ source: 'known_issue.log#8',
83
+ category: 'validation',
84
+ status: 'mitigated',
85
+ severity: 'high',
86
+ targetCapability: 'Premature acceptance guard and done-level harness validation'
87
+ }
88
+ ];
89
+ function createOperationalDebtReport(projectRoot) {
90
+ const tasks = (0, task_capsule_1.listTaskCapsules)(projectRoot);
91
+ return {
92
+ schemaVersion: 'hadara.operational_debt.v1',
93
+ command: 'operational-debt.report',
94
+ ok: true,
95
+ records: exports.OPERATIONAL_DEBT_RECORDS,
96
+ aggregate: createOperationalDebtAggregate(exports.OPERATIONAL_DEBT_RECORDS),
97
+ capsuleSizeIndicators: tasks.map((task) => measureCapsuleSize(projectRoot, task)),
98
+ issues: tasks.flatMap((task) => detectPrematureAcceptance(projectRoot, task))
99
+ };
100
+ }
101
+ function createOperationalDebtShowReport(projectRoot, id) {
102
+ const record = createOperationalDebtReport(projectRoot).records.find((candidate) => candidate.id === id) ?? null;
103
+ return {
104
+ schemaVersion: 'hadara.operational_debt.show.v1',
105
+ command: 'operational-debt.show',
106
+ ok: record !== null,
107
+ id,
108
+ record,
109
+ issues: record
110
+ ? []
111
+ : [
112
+ {
113
+ severity: 'error',
114
+ code: 'OPERATIONAL_DEBT_NOT_FOUND',
115
+ message: `Operational debt record not found: ${id}`
116
+ }
117
+ ]
118
+ };
119
+ }
120
+ function createReleaseGateReport(projectRoot, mode = 'advisory') {
121
+ const debt = createOperationalDebtReport(projectRoot);
122
+ const highOpen = debt.records.filter((record) => isOpenDebt(record) && record.severity === 'high');
123
+ const blocking = mode === 'strict' && highOpen.length > 0;
124
+ const readiness = createReleaseReadinessChecks(projectRoot, mode);
125
+ const debtIssues = highOpen.length > 0
126
+ ? [
127
+ {
128
+ severity: blocking ? 'error' : 'warning',
129
+ code: 'OPEN_HIGH_OPERATIONAL_DEBT',
130
+ message: `${highOpen.length} open high-severity operational debt record(s) remain.`
131
+ }
132
+ ]
133
+ : [];
134
+ const issues = [...readiness.issues, ...debtIssues];
135
+ return {
136
+ schemaVersion: 'hadara.releaseGate.v1',
137
+ command: 'release.gate',
138
+ mode,
139
+ ok: !blocking && readiness.checks.every((check) => check.status !== 'error'),
140
+ checks: [
141
+ ...readiness.checks,
142
+ {
143
+ code: 'OPEN_HIGH_OPERATIONAL_DEBT',
144
+ name: 'No high severity operational debt',
145
+ status: highOpen.length > 0 ? (blocking ? 'error' : 'warning') : 'passed',
146
+ summary: highOpen.length > 0 ? `${highOpen.map((record) => record.id).join(', ')} remain open.` : 'No open high-severity operational debt records.'
147
+ }
148
+ ],
149
+ issues
150
+ };
151
+ }
152
+ function createReleaseReadinessChecks(projectRoot, mode) {
153
+ const packageJson = readJsonObject(node_path_1.default.join(projectRoot, 'package.json'));
154
+ const ciWorkflow = readOptionalText(node_path_1.default.join(projectRoot, '.github', 'workflows', 'ci.yml'));
155
+ const v1Schemas = readOptionalText(node_path_1.default.join(projectRoot, 'docs', 'V1_0_IMPLEMENTATION_SCHEMAS.md'));
156
+ const developmentSlices = readOptionalText(node_path_1.default.join(projectRoot, 'docs', 'DEVELOPMENT_SLICES.md'));
157
+ const projectState = readOptionalText(node_path_1.default.join(projectRoot, 'docs', 'PROJECT_STATE.md'));
158
+ const testStrategy = readOptionalText(node_path_1.default.join(projectRoot, 'docs', 'TEST_STRATEGY.md'));
159
+ const releaseReadiness = readOptionalText(node_path_1.default.join(projectRoot, 'docs', 'RELEASE_READINESS.md'));
160
+ const validationHistory = readOptionalText(node_path_1.default.join(projectRoot, 'docs', 'VALIDATION_HISTORY.md'));
161
+ const licenseText = readOptionalText(node_path_1.default.join(projectRoot, 'LICENSE'));
162
+ const evidence = readReleaseEvidenceRecords(projectRoot);
163
+ const checks = [
164
+ checkPackageBin(packageJson, mode),
165
+ checkPackageScripts(packageJson, mode),
166
+ checkNodePolicy(packageJson, ciWorkflow, mode),
167
+ checkCiWorkflow(ciWorkflow, mode),
168
+ checkCleanCheckoutPolicy(v1Schemas, developmentSlices, testStrategy, mode),
169
+ checkPackageSmokeArtifactBoundary(testStrategy, mode),
170
+ checkPackageSmokeCommandSurface(testStrategy, mode),
171
+ checkPackageMetadataReadiness(packageJson, licenseText, testStrategy, releaseReadiness, validationHistory, mode),
172
+ checkReleaseWorkflowTargetDecision(releaseReadiness, mode),
173
+ checkInstallerSurfaceAndSchema(releaseReadiness, mode),
174
+ checkInstallMatrixSmokePlan(releaseReadiness, mode),
175
+ checkPackageSmokeEvidence(evidence, mode),
176
+ checkCleanCheckoutSmokeEvidence(evidence, mode),
177
+ checkReleaseArtifactEvidence(evidence, mode),
178
+ checkInstallMatrixSmokeEvidence(),
179
+ checkGeneratedArtifactPolicy(projectState, developmentSlices, mode),
180
+ checkRemoteCiObservation(testStrategy, validationHistory, mode)
181
+ ];
182
+ const issues = checks
183
+ .filter((check) => check.status !== 'passed')
184
+ .map((check) => ({
185
+ severity: check.status === 'error' ? 'error' : 'warning',
186
+ code: releaseReadinessIssueCode(check.code),
187
+ message: `${check.name}: ${check.summary}`
188
+ }));
189
+ return { checks, issues };
190
+ }
191
+ function releaseReadinessIssueCode(checkCode) {
192
+ if (checkCode === 'REMOTE_CI_OBSERVATION')
193
+ return 'REMOTE_CI_OBSERVATION_UNRECORDED';
194
+ if (checkCode === 'PACKAGE_SMOKE_ARTIFACT_BOUNDARY')
195
+ return 'PACKAGE_SMOKE_ARTIFACT_BOUNDARY_UNCLEAR';
196
+ if (checkCode === 'PACKAGE_SMOKE_COMMAND_SURFACE')
197
+ return 'PACKAGE_SMOKE_COMMAND_SURFACE_UNCLEAR';
198
+ if (checkCode === 'PACKAGE_METADATA_RELEASE_READINESS')
199
+ return 'PACKAGE_METADATA_RELEASE_READINESS_UNCLEAR';
200
+ if (checkCode === 'CI_RELEASE_WORKFLOW_TARGET_DECISION')
201
+ return 'CI_RELEASE_WORKFLOW_TARGET_DECISION_UNCLEAR';
202
+ if (checkCode === 'INSTALLER_SCRIPT_SURFACE_SCHEMA')
203
+ return 'INSTALLER_SCRIPT_SURFACE_SCHEMA_UNCLEAR';
204
+ if (checkCode === 'INSTALL_MATRIX_SMOKE_PLAN')
205
+ return 'INSTALL_MATRIX_SMOKE_PLAN_UNCLEAR';
206
+ if (checkCode === 'PACKAGE_SMOKE_EVIDENCE')
207
+ return 'PACKAGE_SMOKE_EVIDENCE_MISSING';
208
+ if (checkCode === 'CLEAN_CHECKOUT_SMOKE_EVIDENCE')
209
+ return 'CLEAN_CHECKOUT_SMOKE_EVIDENCE_MISSING';
210
+ if (checkCode === 'RELEASE_ARTIFACT_EVIDENCE')
211
+ return 'RELEASE_ARTIFACT_EVIDENCE_MISSING';
212
+ if (checkCode === 'INSTALL_MATRIX_SMOKE_EVIDENCE')
213
+ return 'INSTALL_MATRIX_SMOKE_EVIDENCE_MISSING';
214
+ return checkCode;
215
+ }
216
+ function checkPackageBin(packageJson, mode) {
217
+ const bin = isRecord(packageJson?.bin) ? packageJson.bin : {};
218
+ const ok = bin.hadara === './dist/cli/main.js';
219
+ return {
220
+ code: 'PACKAGE_BIN_MISSING',
221
+ name: 'Package bin entry',
222
+ status: ok ? 'passed' : readinessFailureStatus(mode),
223
+ summary: ok ? 'package.json exposes hadara at ./dist/cli/main.js.' : 'package.json must expose bin.hadara as ./dist/cli/main.js.'
224
+ };
225
+ }
226
+ function checkPackageScripts(packageJson, mode) {
227
+ const scripts = isRecord(packageJson?.scripts) ? packageJson.scripts : {};
228
+ const required = ['build', 'test', 'test:contract', 'test:harness', 'check'];
229
+ const missing = required.filter((script) => typeof scripts[script] !== 'string' || scripts[script].trim() === '');
230
+ return {
231
+ code: 'VALIDATION_SCRIPT_MISSING',
232
+ name: 'Package validation scripts',
233
+ status: missing.length === 0 ? 'passed' : readinessFailureStatus(mode),
234
+ summary: missing.length === 0 ? `${required.join(', ')} scripts are defined.` : `Missing package scripts: ${missing.join(', ')}.`
235
+ };
236
+ }
237
+ function checkNodePolicy(packageJson, ciWorkflow, mode) {
238
+ const devDependencies = isRecord(packageJson?.devDependencies) ? packageJson.devDependencies : {};
239
+ const nodeTypes = String(devDependencies['@types/node'] ?? '');
240
+ const ciUsesNode22 = ciWorkflow !== null && /node-version:\s*22\b/.test(ciWorkflow);
241
+ const ok = nodeTypes.startsWith('^22') && ciUsesNode22;
242
+ return {
243
+ code: 'NODE_POLICY_UNCLEAR',
244
+ name: 'Node version policy',
245
+ status: ok ? 'passed' : readinessFailureStatus(mode),
246
+ summary: ok ? 'Development typings and CI target Node 22.' : 'Node 22 must be reflected in dev dependencies and CI.'
247
+ };
248
+ }
249
+ function checkCiWorkflow(ciWorkflow, mode) {
250
+ const missing = [
251
+ ['actions/setup-node@v4', ciWorkflow?.includes('actions/setup-node@v4')],
252
+ ['npm ci', ciWorkflow?.includes('npm ci')],
253
+ ['npm run check', ciWorkflow?.includes('npm run check')]
254
+ ]
255
+ .filter(([, present]) => !present)
256
+ .map(([name]) => name);
257
+ return {
258
+ code: 'CI_CLEAN_INSTALL_UNCLEAR',
259
+ name: 'CI clean install check',
260
+ status: missing.length === 0 ? 'passed' : readinessFailureStatus(mode),
261
+ summary: missing.length === 0 ? 'CI installs dependencies cleanly and runs npm run check.' : `CI workflow is missing: ${missing.join(', ')}.`
262
+ };
263
+ }
264
+ function checkCleanCheckoutPolicy(v1Schemas, developmentSlices, testStrategy, mode) {
265
+ const ok = includesAll(v1Schemas, ['npm ci', 'npm run check', 'doctor --json', 'ops status --json']) &&
266
+ includesAny(developmentSlices, ['clean checkout smoke', 'clean-checkout smoke']) &&
267
+ includesAll(testStrategy, [
268
+ 'Clean Checkout Package Smoke Plan',
269
+ 'npm ci',
270
+ 'npm run build',
271
+ 'node dist/cli/main.js doctor --json',
272
+ 'node dist/cli/main.js ops status --json',
273
+ 'node dist/cli/main.js release gate --mode strict --json',
274
+ 'no packaging or release execution'
275
+ ]);
276
+ return {
277
+ code: 'CLEAN_CHECKOUT_SMOKE_UNCLEAR',
278
+ name: 'Clean checkout smoke policy',
279
+ status: ok ? 'passed' : readinessFailureStatus(mode),
280
+ summary: ok
281
+ ? 'Release planning documents the clean-checkout package smoke sequence.'
282
+ : 'Release planning must document clean-checkout package smoke expectations.'
283
+ };
284
+ }
285
+ function checkPackageSmokeArtifactBoundary(testStrategy, mode) {
286
+ const ok = includesAll(testStrategy, [
287
+ 'Executable Package Smoke Artifact Boundary',
288
+ 'Allowed workspace',
289
+ '/tmp/hadara-package-smoke/<run-id>',
290
+ 'Package artifact paths',
291
+ 'tasks/<task-id>/artifacts/package-smoke/',
292
+ 'Redaction and audit handling',
293
+ 'Evidence/report shape',
294
+ 'hadara.packageSmoke.v1',
295
+ 'performs no package-smoke execution'
296
+ ]);
297
+ return {
298
+ code: 'PACKAGE_SMOKE_ARTIFACT_BOUNDARY',
299
+ name: 'Package smoke artifact boundary',
300
+ status: ok ? 'passed' : readinessFailureStatus(mode),
301
+ summary: ok
302
+ ? 'Executable package-smoke artifact and evidence boundaries are documented before implementation.'
303
+ : 'Executable package-smoke workspace, artifact, redaction/audit, and evidence boundaries must be documented before implementation.'
304
+ };
305
+ }
306
+ function checkPackageSmokeCommandSurface(testStrategy, mode) {
307
+ const ok = includesAll(testStrategy, [
308
+ 'Package Smoke Command Surface',
309
+ 'hadara package smoke --dry-run --json',
310
+ 'hadara package smoke --task <task-id> --json',
311
+ 'hadara package smoke --workspace /tmp/hadara-package-smoke/<run-id> --json',
312
+ 'hadara package smoke --from ./dist-release/hadara-0.1.0-rc.0.tgz --json',
313
+ 'hadara package smoke --keep-temp --json',
314
+ 'Do not use `hadara release smoke` as the primary command surface',
315
+ '`--timeout <seconds>`',
316
+ '`--attach-evidence`',
317
+ '`--private-logs`',
318
+ 'Package smoke must not be callable from MCP by default',
319
+ 'The release gate must not call `hadara package smoke`'
320
+ ]);
321
+ return {
322
+ code: 'PACKAGE_SMOKE_COMMAND_SURFACE',
323
+ name: 'Package smoke command surface',
324
+ status: ok ? 'passed' : readinessFailureStatus(mode),
325
+ summary: ok
326
+ ? '`hadara package smoke` command naming, flags, approval, cleanup, failure, evidence, and MCP boundaries are documented.'
327
+ : 'Package-smoke command naming, flags, approval, cleanup, failure, evidence, and MCP boundaries must be documented before implementation.'
328
+ };
329
+ }
330
+ function checkPackageMetadataReadiness(packageJson, licenseText, testStrategy, releaseReadiness, validationHistory, mode) {
331
+ const bin = isRecord(packageJson?.bin) ? packageJson.bin : {};
332
+ const files = Array.isArray(packageJson?.files) ? packageJson.files.filter((entry) => typeof entry === 'string') : [];
333
+ const bootstrapMetadataOk = packageJson?.name === 'hadara' &&
334
+ packageJson?.version === '0.0.0-bootstrap' &&
335
+ packageJson?.private === true &&
336
+ bin.hadara === './dist/cli/main.js';
337
+ const releaseCandidateMetadataOk = packageJson?.name === 'hadara' &&
338
+ /^0\.1\.0-rc\.\d+$/.test(String(packageJson?.version ?? '')) &&
339
+ packageJson?.private === false &&
340
+ packageJson?.license === 'MIT' &&
341
+ bin.hadara === './dist/cli/main.js' &&
342
+ includesAll(files.join('\n'), ['dist/', 'README.md', 'LICENSE', 'package.json']) &&
343
+ licenseText !== null &&
344
+ includesAny(validationHistory, ['hadara.packageSmoke.v1', 'PACKAGE_SMOKE_EVIDENCE']);
345
+ const metadataMarkers = [
346
+ 'Package Metadata Release Readiness',
347
+ 'Package name decision: `hadara`',
348
+ 'npm registry observation: `npm view hadara name version --registry=https://registry.npmjs.org` returned 404 on 2026-05-28',
349
+ 'Current version is `0.1.0-rc.0`',
350
+ 'Current package is `private: false`',
351
+ 'Current binary remains `bin.hadara` at `./dist/cli/main.js`',
352
+ 'Current `files` whitelist is `dist/`, `README.md`, `LICENSE`, and `package.json`',
353
+ 'Bootstrap metadata mode: version `0.0.0-bootstrap`, `private: true`, no package publishability',
354
+ 'Release-candidate metadata mode: version `0.1.0-rc.N`, `private: false`, `files` whitelist present, `LICENSE` present, package smoke evidence present',
355
+ 'Scoped fallback decision: do not silently switch names',
356
+ 'Version policy: first release-candidate target is `0.1.0-rc.0`; first stable target is `0.1.0`',
357
+ 'T-0142 transitions `private` to false only after the package files whitelist, root README, license decision, and package-smoke evidence gates exist',
358
+ 'Final `files` whitelist target: `dist/`, `README.md`, `LICENSE`, `package.json`, plus installer and portable files only after those files exist',
359
+ 'Do not add `files` entries for missing installer or portable paths in T-0127',
360
+ 'MIT license decision: adopt MIT; `LICENSE` exists and is included in the package whitelist',
361
+ 'Publish target decision: npm package first, GitHub Release second, Docker image deferred',
362
+ 'Installed CLI verification must use `hadara doctor --json`',
363
+ 'T-0142 performs no publish, no GitHub Release creation, no Docker image build, and no registry mutation; it transitions metadata and regenerates reduced release evidence only',
364
+ 'Before adding more T-0128+ release/install/package-smoke readiness markers, prefer moving the structured readiness source to `docs/RELEASE_READINESS.md` or `docs/release-readiness.json`'
365
+ ];
366
+ const docsOk = includesAll(testStrategy, metadataMarkers) || includesAll(releaseReadiness, metadataMarkers);
367
+ const ok = (bootstrapMetadataOk || releaseCandidateMetadataOk) && docsOk;
368
+ return {
369
+ code: 'PACKAGE_METADATA_RELEASE_READINESS',
370
+ name: 'Package metadata release readiness',
371
+ status: ok ? 'passed' : readinessFailureStatus(mode),
372
+ summary: ok
373
+ ? 'Package name, release-candidate version, private transition, files target, license path, publish target, and installed CLI verification decisions are documented without publishing.'
374
+ : 'Package metadata release-readiness decisions must be documented before publishability is accepted.'
375
+ };
376
+ }
377
+ function checkInstallerSurfaceAndSchema(releaseReadiness, mode) {
378
+ const ok = includesAll(releaseReadiness, [
379
+ 'Installer Script Surface and Schema',
380
+ '`scripts/install.sh`',
381
+ '`scripts/install.ps1`',
382
+ '`portable/bin/hadara`',
383
+ '`portable/bin/hadara.cmd`',
384
+ '`portable/bin/hadara.ps1`',
385
+ 'Installer scripts install or plan installation from a tarball or directory',
386
+ 'Installer scripts must support dry-run planning before mutation',
387
+ 'Installer scripts must emit `hadara.install.plan.v1` JSON for dry-run planning',
388
+ 'Installer scripts must not use `sudo` by default',
389
+ 'Installer scripts must not force `npm install -g`',
390
+ 'Installer scripts must not mutate shell profiles or PATH by default',
391
+ 'Portable launchers invoke an installed or portable HADARA bundle',
392
+ 'Portable launchers do not install dependencies',
393
+ 'Portable launchers do not mutate PATH',
394
+ 'Portable launchers do not modify project files',
395
+ 'Linux/POSIX/WSL prefix suggestion: `~/.local/share/hadara`',
396
+ 'Linux/POSIX/WSL bin link suggestion: `~/.local/bin/hadara`',
397
+ 'Windows prefix suggestion: `%LOCALAPPDATA%\\HADARA`',
398
+ 'Windows cmd launcher suggestion: `%LOCALAPPDATA%\\HADARA\\bin\\hadara.cmd`',
399
+ 'Windows PowerShell launcher suggestion: `%LOCALAPPDATA%\\HADARA\\bin\\hadara.ps1`',
400
+ 'Default POSIX/WSL/Windows install paths are suggestions, not silent decisions',
401
+ 'Windows USB portable root: user-selected removable drive, for example `L:\\HADARA`',
402
+ 'WSL USB portable root for `--platform usb`: user-selected mounted removable drive, for example `/mnt/l/HADARA`',
403
+ 'The drive letter or mount path must not be assumed',
404
+ 'USB install roots must be explicitly provided',
405
+ '`--platform wsl` uses Linux-style default install suggestions',
406
+ 'Installer plans must validate Node 22',
407
+ 'WSL install plans must reject Windows `node.exe` shims',
408
+ 'Schema id: `hadara.install.plan.v1`',
409
+ 'Target paths must be public path references, not raw absolute path strings',
410
+ '`target.prefix.pathRedacted: true` is required for public install-plan output',
411
+ '`target.launcher.pathRedacted: true` is required for public install-plan output',
412
+ '`source.pathRedacted: true` is required when source path details appear in public install-plan output',
413
+ '`execution.executeEnabled` must state whether mutation is available to the current command implementation',
414
+ '`mode: execute` is schema-reserved only until an explicit later capsule implements mutation',
415
+ 'T-0129 dry-run implementation must reject execute mode or return `INSTALL_EXECUTION_DISABLED`',
416
+ 'The schema fixture documents a future execute mode but does not authorize installer execution',
417
+ 'The release gate checks installer surface and schema markers only',
418
+ 'The release gate must not execute `scripts/install.sh`',
419
+ 'The release gate must not execute `scripts/install.ps1`'
420
+ ]);
421
+ return {
422
+ code: 'INSTALLER_SCRIPT_SURFACE_SCHEMA',
423
+ name: 'Installer script surface and schema',
424
+ status: ok ? 'passed' : readinessFailureStatus(mode),
425
+ summary: ok
426
+ ? 'Installer script paths, portable launchers, install locations, Node/WSL checks, and install plan schema are documented without install mutation.'
427
+ : 'Installer script paths, portable launchers, install locations, Node/WSL checks, and install plan schema must be documented before implementation.'
428
+ };
429
+ }
430
+ function checkReleaseWorkflowTargetDecision(releaseReadiness, mode) {
431
+ const ok = includesAll(releaseReadiness, [
432
+ 'CI Release Workflow Target Decision',
433
+ 'Primary release target: npm package',
434
+ 'Secondary release target: GitHub Release with tarball, checksum, and manifest',
435
+ 'Deferred release target: Docker image',
436
+ 'npm publish token name: `NPM_TOKEN`',
437
+ 'GitHub Release token name: `GITHUB_TOKEN` or `HADARA_GITHUB_RELEASE_TOKEN`',
438
+ 'Token values must never be written to repository files, public evidence, release artifacts, logs, manifests, or context export',
439
+ 'Publish/deploy remains explicit approval only',
440
+ 'T-0139 performs no publish, no GitHub Release creation, no Docker image build, no registry mutation, no GitHub API call, and no token loading',
441
+ 'Evidence freshness must compare evidence to the release candidate window',
442
+ 'Evidence cross-check should follow this order: record exists, artifact exists, artifact schema valid, `sourceReport.ok` true when present, category/mode/result match the expected check',
443
+ 'Release artifact evidence flow must be explicit: run `hadara release artifact --execute --json --output dist-release`'
444
+ ]);
445
+ return {
446
+ code: 'CI_RELEASE_WORKFLOW_TARGET_DECISION',
447
+ name: 'CI/release workflow target decision',
448
+ status: ok ? 'passed' : readinessFailureStatus(mode),
449
+ summary: ok
450
+ ? 'Release targets, token names, approval boundary, and T-0140 evidence hardening requirements are documented.'
451
+ : 'Release workflow targets, token names, approval boundary, and evidence hardening requirements must be documented before release scripts.'
452
+ };
453
+ }
454
+ function checkInstallMatrixSmokePlan(releaseReadiness, mode) {
455
+ const ok = includesAll(releaseReadiness, [
456
+ 'Install Matrix Smoke Plan',
457
+ 'T-0130 defines install-matrix smoke planning only',
458
+ 'Matrix row: Linux source checkout',
459
+ 'Matrix row: Linux package install',
460
+ 'Matrix row: WSL source checkout',
461
+ 'Matrix row: Windows source checkout',
462
+ 'Matrix row: Windows package install',
463
+ 'Matrix row: USB portable on Windows',
464
+ 'Matrix row: USB portable on WSL',
465
+ 'Matrix row: installed CLI major-feature smoke',
466
+ 'Docker/Linux validation does not replace real Windows validation',
467
+ 'USB rows must require explicit user-selected USB roots',
468
+ 'Package-install rows are blocked until package smoke and release artifacts exist',
469
+ 'Matrix evidence must record platform, source kind, installer/package form, command form, and reduced public result',
470
+ 'Raw logs and private paths must stay temporary or private/local',
471
+ 'The release gate must not execute install matrix smoke'
472
+ ]);
473
+ return {
474
+ code: 'INSTALL_MATRIX_SMOKE_PLAN',
475
+ name: 'Install matrix smoke plan',
476
+ status: ok ? 'passed' : readinessFailureStatus(mode),
477
+ summary: ok
478
+ ? 'Install matrix rows, platform boundaries, evidence shape, and non-execution release-gate behavior are documented.'
479
+ : 'Install matrix smoke rows and evidence boundaries must be documented before execution.'
480
+ };
481
+ }
482
+ function checkPackageSmokeEvidence(evidence, mode) {
483
+ const match = findLatestEvidence(evidence, (record) => record.result === 'passed' &&
484
+ record.visibility === 'public' &&
485
+ (includesAll(record.summary, ['package smoke', '--execute']) ||
486
+ Boolean(record.evidencePath?.includes('artifacts/package-smoke/'))) &&
487
+ (includesAny(record.summary, ['--attach-evidence', 'artifacts/package-smoke', 'hadara.packageSmoke.v1']) ||
488
+ Boolean(record.evidencePath?.includes('artifacts/package-smoke/'))));
489
+ return createEvidenceCheck('PACKAGE_SMOKE_EVIDENCE', 'Package smoke evidence', match, mode, 'Latest package-smoke evidence is recorded', 'Record passed public package-smoke execution evidence before release readiness is frozen.');
490
+ }
491
+ function checkCleanCheckoutSmokeEvidence(evidence, mode) {
492
+ const match = findLatestEvidence(evidence, (record) => record.result === 'passed' &&
493
+ record.visibility === 'public' &&
494
+ (includesAll(record.summary, ['smoke clean-checkout', '--execute']) ||
495
+ Boolean(record.evidencePath?.includes('artifacts/clean-checkout-smoke/'))) &&
496
+ (includesAny(record.summary, ['--attach-evidence', 'artifacts/clean-checkout-smoke', 'hadara.cleanCheckoutSmoke.v1']) ||
497
+ Boolean(record.evidencePath?.includes('artifacts/clean-checkout-smoke/'))));
498
+ return createEvidenceCheck('CLEAN_CHECKOUT_SMOKE_EVIDENCE', 'Clean checkout smoke evidence', match, mode, 'Latest clean-checkout smoke evidence is recorded', 'Record passed public clean-checkout smoke evidence before release readiness is frozen.');
499
+ }
500
+ function checkReleaseArtifactEvidence(evidence, mode) {
501
+ const match = findLatestEvidence(evidence, (record) => record.result === 'passed' &&
502
+ record.visibility === 'public' &&
503
+ includesAll(record.summary, ['release artifact', '--execute']) &&
504
+ includesAny(record.summary, ['generated tarball/checksum/manifest', 'retained-output', 'hadara.releaseArtifact.v1']));
505
+ return createEvidenceCheck('RELEASE_ARTIFACT_EVIDENCE', 'Release artifact evidence', match, mode, 'Latest release artifact build evidence is recorded', 'Record passed public release artifact build evidence before release readiness is frozen.');
506
+ }
507
+ function checkInstallMatrixSmokeEvidence() {
508
+ return {
509
+ code: 'INSTALL_MATRIX_SMOKE_EVIDENCE',
510
+ name: 'Install matrix smoke evidence',
511
+ status: 'passed',
512
+ summary: 'Install-matrix evidence enforcement is deferred until an executable install-matrix smoke surface exists.'
513
+ };
514
+ }
515
+ function createEvidenceCheck(code, name, match, mode, passedSummary, missingSummary) {
516
+ if (!match) {
517
+ return {
518
+ code,
519
+ name,
520
+ status: readinessFailureStatus(mode),
521
+ summary: missingSummary
522
+ };
523
+ }
524
+ const artifactNote = match.artifactSchemaValid === undefined ? '' : match.artifactSchemaValid ? '; linked summary artifact is schema-valid' : '; linked summary artifact was not schema-valid';
525
+ return {
526
+ code,
527
+ name,
528
+ status: match.artifactSchemaValid === false ? readinessFailureStatus(mode) : 'passed',
529
+ summary: `${passedSummary}: ${match.record.taskId} at ${match.record.time}${artifactNote}.`
530
+ };
531
+ }
532
+ function checkGeneratedArtifactPolicy(projectState, developmentSlices, mode) {
533
+ const ok = includesAll(projectState, ['contextPath: null', '.hadara/local/tui/', 'read-only local API routes']) && includesAll(developmentSlices, ['without writing generated context files']);
534
+ return {
535
+ code: 'GENERATED_ARTIFACT_POLICY_UNCLEAR',
536
+ name: 'Generated artifact policy',
537
+ status: ok ? 'passed' : readinessFailureStatus(mode),
538
+ summary: ok
539
+ ? 'Context export, dashboard APIs, and TUI cache boundaries are documented as non-committed/generated or read-only surfaces.'
540
+ : 'Generated context/dashboard/cache artifact boundaries must be documented before release.'
541
+ };
542
+ }
543
+ function checkRemoteCiObservation(testStrategy, validationHistory, mode) {
544
+ const ok = includesAll(testStrategy, ['Remote CI observation', 'local Docker validation remains the primary reproducible check']) &&
545
+ includesAll(validationHistory, ['GitHub Actions CI run', 'actions/runs/']);
546
+ return {
547
+ code: 'REMOTE_CI_OBSERVATION',
548
+ name: 'Remote CI observation evidence',
549
+ status: ok ? 'passed' : readinessFailureStatus(mode),
550
+ summary: ok
551
+ ? 'Remote GitHub Actions status is recorded separately from local release-gate checks.'
552
+ : 'Record a recent remote GitHub Actions observation and keep it distinct from local Docker validation.'
553
+ };
554
+ }
555
+ function readReleaseEvidenceRecords(projectRoot) {
556
+ return (0, task_capsule_1.listTaskCapsules)(projectRoot).flatMap((task) => readTaskEvidenceRecords(task.dir));
557
+ }
558
+ function readTaskEvidenceRecords(taskDir) {
559
+ const evidencePath = node_path_1.default.join(taskDir, 'evidence.jsonl');
560
+ if (!node_fs_1.default.existsSync(evidencePath))
561
+ return [];
562
+ return node_fs_1.default
563
+ .readFileSync(evidencePath, 'utf8')
564
+ .split(/\r?\n/)
565
+ .map((line) => {
566
+ if (line.trim() === '')
567
+ return null;
568
+ try {
569
+ const parsed = JSON.parse(line);
570
+ if (!isRecord(parsed))
571
+ return null;
572
+ if (parsed.schemaVersion !== 'hadara.evidence.v1')
573
+ return null;
574
+ if (typeof parsed.time !== 'string' || typeof parsed.summary !== 'string')
575
+ return null;
576
+ if (typeof parsed.taskId !== 'string' || typeof parsed.kind !== 'string')
577
+ return null;
578
+ if (typeof parsed.result !== 'string' || typeof parsed.visibility !== 'string')
579
+ return null;
580
+ return {
581
+ taskId: parsed.taskId,
582
+ taskDir,
583
+ time: parsed.time,
584
+ kind: parsed.kind,
585
+ summary: parsed.summary,
586
+ result: parsed.result,
587
+ visibility: parsed.visibility,
588
+ ...(typeof parsed.evidencePath === 'string' ? { evidencePath: parsed.evidencePath } : {})
589
+ };
590
+ }
591
+ catch {
592
+ return null;
593
+ }
594
+ })
595
+ .filter((record) => record !== null);
596
+ }
597
+ function findLatestEvidence(evidence, predicate) {
598
+ const matches = evidence.filter(predicate).sort((a, b) => b.time.localeCompare(a.time));
599
+ const record = matches[0];
600
+ if (!record)
601
+ return null;
602
+ return {
603
+ record,
604
+ ...validateLinkedEvidenceArtifact(record)
605
+ };
606
+ }
607
+ function validateLinkedEvidenceArtifact(record) {
608
+ if (!record.evidencePath)
609
+ return {};
610
+ const artifactPath = node_path_1.default.resolve(record.taskDir, record.evidencePath);
611
+ if (!artifactPath.startsWith(`${node_path_1.default.resolve(record.taskDir)}${node_path_1.default.sep}`) || !node_fs_1.default.existsSync(artifactPath))
612
+ return {};
613
+ try {
614
+ const parsed = JSON.parse(node_fs_1.default.readFileSync(artifactPath, 'utf8'));
615
+ if (!isRecord(parsed) || typeof parsed.schemaVersion !== 'string')
616
+ return { artifactSchemaValid: false };
617
+ if (parsed.schemaVersion !== 'hadara.smokeEvidenceSummary.v1')
618
+ return {};
619
+ return { artifactSchemaValid: (0, schema_1.validateSchema)('hadara.smokeEvidenceSummary.v1', parsed).ok };
620
+ }
621
+ catch {
622
+ return { artifactSchemaValid: false };
623
+ }
624
+ }
625
+ function readinessFailureStatus(mode) {
626
+ return mode === 'strict' ? 'error' : 'warning';
627
+ }
628
+ function readJsonObject(filePath) {
629
+ try {
630
+ const parsed = JSON.parse(node_fs_1.default.readFileSync(filePath, 'utf8'));
631
+ return isRecord(parsed) ? parsed : null;
632
+ }
633
+ catch {
634
+ return null;
635
+ }
636
+ }
637
+ function readOptionalText(filePath) {
638
+ try {
639
+ return node_fs_1.default.readFileSync(filePath, 'utf8');
640
+ }
641
+ catch {
642
+ return null;
643
+ }
644
+ }
645
+ function includesAll(text, needles) {
646
+ return text !== null && needles.every((needle) => text.includes(needle));
647
+ }
648
+ function includesAny(text, needles) {
649
+ return text !== null && needles.some((needle) => text.includes(needle));
650
+ }
651
+ function isRecord(value) {
652
+ return typeof value === 'object' && value !== null && !Array.isArray(value);
653
+ }
654
+ function createOperationalDebtAggregate(records) {
655
+ const aggregate = {
656
+ total: records.length,
657
+ open: 0,
658
+ tracked: 0,
659
+ mitigated: 0,
660
+ candidate: 0,
661
+ highOpen: 0,
662
+ bySeverity: {
663
+ high: 0,
664
+ medium: 0,
665
+ low: 0
666
+ }
667
+ };
668
+ for (const record of records) {
669
+ aggregate[record.status] += 1;
670
+ aggregate.bySeverity[record.severity] += 1;
671
+ if (isOpenDebt(record))
672
+ aggregate.open += 1;
673
+ if (isOpenDebt(record) && record.severity === 'high')
674
+ aggregate.highOpen += 1;
675
+ }
676
+ return aggregate;
677
+ }
678
+ function isOpenDebt(record) {
679
+ return record.status !== 'mitigated';
680
+ }
681
+ function measureCapsuleSize(projectRoot, task) {
682
+ const files = node_fs_1.default
683
+ .readdirSync(task.dir, { withFileTypes: true })
684
+ .filter((entry) => entry.isFile())
685
+ .map((entry) => node_path_1.default.join(task.dir, entry.name));
686
+ const totals = files.reduce((acc, filePath) => {
687
+ const content = node_fs_1.default.readFileSync(filePath, 'utf8');
688
+ return {
689
+ bytes: acc.bytes + Buffer.byteLength(content, 'utf8'),
690
+ lines: acc.lines + countLines(content)
691
+ };
692
+ }, { bytes: 0, lines: 0 });
693
+ return {
694
+ taskId: task.id,
695
+ capsule: toPortablePath(node_path_1.default.relative(projectRoot, task.dir)),
696
+ fileCount: files.length,
697
+ lineCount: totals.lines,
698
+ byteCount: totals.bytes,
699
+ size: classifyCapsuleSize(totals.lines)
700
+ };
701
+ }
702
+ function detectPrematureAcceptance(projectRoot, task) {
703
+ const taskPath = node_path_1.default.join(task.dir, 'TASK.md');
704
+ const acceptancePath = node_path_1.default.join(task.dir, 'ACCEPTANCE.md');
705
+ const evidencePath = node_path_1.default.join(task.dir, 'evidence.jsonl');
706
+ if (!node_fs_1.default.existsSync(taskPath) || !node_fs_1.default.existsSync(acceptancePath))
707
+ return [];
708
+ const taskStatus = readTaskStatus(taskPath);
709
+ const acceptance = node_fs_1.default.readFileSync(acceptancePath, 'utf8');
710
+ const checkedCount = acceptance.match(/-\s+\[[xX]\]/g)?.length ?? 0;
711
+ const evidenceCount = countValidEvidenceRecords(evidencePath);
712
+ if (checkedCount > 0 && (taskStatus !== 'Done' || evidenceCount === 0)) {
713
+ return [
714
+ {
715
+ severity: 'warning',
716
+ code: 'PREMATURE_ACCEPTANCE_CHECKED',
717
+ message: `${task.id} has checked acceptance boxes before Done status or evidence records.`,
718
+ path: toPortablePath(node_path_1.default.relative(projectRoot, acceptancePath))
719
+ }
720
+ ];
721
+ }
722
+ return [];
723
+ }
724
+ function classifyCapsuleSize(lineCount) {
725
+ if (lineCount < 80)
726
+ return 'tiny';
727
+ if (lineCount > 700)
728
+ return 'large';
729
+ return 'standard';
730
+ }
731
+ function readTaskStatus(taskPath) {
732
+ const content = node_fs_1.default.readFileSync(taskPath, 'utf8');
733
+ const match = content.match(/^## Status\s*\n+([\s\S]*?)(?:\n## |\s*$)/m);
734
+ return match?.[1]?.trim().split(/\r?\n/)[0]?.trim() || 'Unknown';
735
+ }
736
+ function countValidEvidenceRecords(evidencePath) {
737
+ if (!node_fs_1.default.existsSync(evidencePath))
738
+ return 0;
739
+ return node_fs_1.default
740
+ .readFileSync(evidencePath, 'utf8')
741
+ .trim()
742
+ .split(/\r?\n/)
743
+ .filter(Boolean)
744
+ .filter((line) => {
745
+ try {
746
+ const record = JSON.parse(line);
747
+ return (record &&
748
+ typeof record === 'object' &&
749
+ record.schemaVersion === 'hadara.evidence.v1' &&
750
+ typeof record.time === 'string' &&
751
+ typeof record.taskId === 'string' &&
752
+ typeof record.summary === 'string' &&
753
+ typeof record.visibility === 'string');
754
+ }
755
+ catch {
756
+ return false;
757
+ }
758
+ }).length;
759
+ }
760
+ function countLines(content) {
761
+ if (!content)
762
+ return 0;
763
+ return content.split(/\r?\n/).length;
764
+ }
765
+ function toPortablePath(value) {
766
+ return value.split(node_path_1.default.sep).join('/');
767
+ }