hadara 0.3.0-rc.1 → 0.3.0-rc.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -5,7 +5,8 @@
5
5
  </p>
6
6
 
7
7
  <p align="center">
8
- <img alt="Published npm release" src="https://img.shields.io/badge/npm-0.3.0--rc.0-blue">
8
+ <img alt="Published npm release" src="https://img.shields.io/badge/npm-0.3.0--rc.1-blue">
9
+ <img alt="Source candidate" src="https://img.shields.io/badge/source-0.3.0--rc.2-orange">
9
10
  <img alt="Node.js" src="https://img.shields.io/badge/node-%3E%3D22-brightgreen">
10
11
  <img alt="License" src="https://img.shields.io/badge/license-MIT-lightgrey">
11
12
  </p>
@@ -20,13 +21,19 @@ This repository is both the HADARA source checkout and the HADARA protocol works
20
21
 
21
22
  ## Release Status
22
23
 
23
- Current release candidate prepared for operator publish:
24
+ Current published npm release:
24
25
 
25
26
  ```text
26
27
  hadara@0.3.0-rc.1
27
28
  ```
28
29
 
29
- Current published npm release:
30
+ Current source release candidate:
31
+
32
+ ```text
33
+ hadara@0.3.0-rc.2
34
+ ```
35
+
36
+ Previous published npm release:
30
37
 
31
38
  ```text
32
39
  hadara@0.3.0-rc.0
@@ -34,7 +41,7 @@ hadara@0.3.0-rc.0
34
41
 
35
42
  The 0.3.0 line is the Phase 7 Surface Refactor. It organizes HADARA's existing task, evidence, proof, lifecycle, release, and document surfaces so agents can distinguish primary lifecycle commands, diagnostics, advanced surfaces, canonical documents, historical documents, and safe Markdown update boundaries.
36
43
 
37
- Phase 7.x labels are internal implementation phases, not npm release-candidate labels. Publishing `0.3.0-rc.1` or any later release still requires the approval-gated release path and explicit operator confirmation.
44
+ Phase 7.x labels are internal implementation phases, not npm release-candidate labels. Publishing any later release still requires the approval-gated release path and explicit operator confirmation.
38
45
 
39
46
  | Surface | Status |
40
47
  |---|---|
@@ -42,8 +49,9 @@ Phase 7.x labels are internal implementation phases, not npm release-candidate l
42
49
  | `hadara@0.2.0-rc.1` | Previous published npm RC. |
43
50
  | `hadara@0.2.0-rc.2` | Previous published npm RC. |
44
51
  | `hadara@0.2.0-rc.3` | Previous published npm RC. |
45
- | `hadara@0.3.0-rc.0` | Current published npm RC; package metadata lacks the intended discovery fields. |
46
- | `hadara@0.3.0-rc.1` | Current publish candidate; T-0301 prepares the operator-confirmed npm publish path. |
52
+ | `hadara@0.3.0-rc.0` | Previous published npm RC; package metadata lacks the intended discovery fields. |
53
+ | `hadara@0.3.0-rc.1` | Current published npm RC; T-0301 publish evidence verified `npm view` returned `0.3.0-rc.1`. |
54
+ | `hadara@0.3.0-rc.2` | Current source candidate prepared by T-0310; publish remains approval-gated. |
47
55
  | GitHub Release | Secondary target, approval-gated. |
48
56
  | Docker image | Deferred. |
49
57
  | PyPI/Python package | `hadara==0.2.0rc1` published preview bridge. |
@@ -55,10 +63,10 @@ No release command should publish, create a GitHub Release, build Docker images,
55
63
 
56
64
  Requires Node.js 22.
57
65
 
58
- Install the latest published RC before the T-0301 publish step:
66
+ Install the latest published RC:
59
67
 
60
68
  ```bash
61
- npm install -g hadara@0.3.0-rc.0
69
+ npm install -g hadara@0.3.0-rc.1
62
70
  hadara help
63
71
  hadara doctor --json
64
72
  ```
@@ -66,16 +74,8 @@ hadara doctor --json
66
74
  Run without a global install:
67
75
 
68
76
  ```bash
69
- npx hadara@0.3.0-rc.0 help
70
- npx hadara@0.3.0-rc.0 doctor --json
71
- ```
72
-
73
- After the T-0301 npm publish helper verifies `hadara@0.3.0-rc.1` on the registry, install the new RC:
74
-
75
- ```bash
76
- npm install -g hadara@0.3.0-rc.1
77
- hadara help
78
- hadara doctor --json
77
+ npx hadara@0.3.0-rc.1 help
78
+ npx hadara@0.3.0-rc.1 doctor --json
79
79
  ```
80
80
 
81
81
  ## What HADARA Gives You
package/dist/cli/init.js CHANGED
@@ -140,6 +140,7 @@ function createGeneratedScaffoldFiles(profile) {
140
140
  const spec = INIT_PROFILE_SPECS[profile];
141
141
  const docsRegistry = (0, docs_registry_1.createSeedDocumentRegistry)(profile);
142
142
  const files = [
143
+ { path: '.hadara/context/HADARA_CONTEXT.md', content: (0, docs_registry_1.createHadaraContextDoc)(profile) },
143
144
  { path: '.hadara/docs-registry.json', content: (0, docs_registry_1.registryJson)(docsRegistry) },
144
145
  { path: 'docs/DOC_REGISTRY.md', content: (0, docs_registry_1.renderDocRegistryMarkdown)(docsRegistry) },
145
146
  { path: 'docs/PROJECT_STATE.md', content: createProjectStateDoc(profile) },
@@ -169,7 +170,7 @@ function createGeneratedScaffoldFiles(profile) {
169
170
  function createInitDoctorReport(projectRoot) {
170
171
  const issues = [];
171
172
  const actions = [];
172
- const requiredCore = ['AGENTS.md', '.gitignore', 'docs/PROJECT_STATE.md', 'docs/AGENT_HANDOFF.md', 'docs/TASK_BOARD.md', 'docs/IMPLEMENTATION_SOP.md', 'docs/TASK_WORKFLOW_COMMANDS.md'];
173
+ const requiredCore = ['AGENTS.md', '.gitignore', '.hadara/context/HADARA_CONTEXT.md', 'docs/PROJECT_STATE.md', 'docs/AGENT_HANDOFF.md', 'docs/TASK_BOARD.md', 'docs/IMPLEMENTATION_SOP.md', 'docs/TASK_WORKFLOW_COMMANDS.md'];
173
174
  for (const relativePath of requiredCore) {
174
175
  if (!node_fs_1.default.existsSync(node_path_1.default.join(projectRoot, relativePath))) {
175
176
  issues.push({ severity: 'error', code: 'INIT_CORE_DOC_MISSING', path: relativePath, message: `${relativePath} is missing from the init scaffold.` });
@@ -934,6 +935,7 @@ function createArchitectureDoc(profile) {
934
935
  }
935
936
  function createImplementationSopDoc(spec) {
936
937
  const sessionStart = [
938
+ 'Read `.hadara/context/HADARA_CONTEXT.md` as the compact project-local context anchor.',
937
939
  'Read `docs/PROJECT_STATE.md`.',
938
940
  'Read `docs/AGENT_HANDOFF.md`.',
939
941
  'Read `docs/TASK_BOARD.md`.',
@@ -949,6 +951,7 @@ function createImplementationSopDoc(spec) {
949
951
  'Propose or choose the smallest useful implementation slice.'
950
952
  ];
951
953
  const requiredReadingRows = [
954
+ ['`.hadara/context/HADARA_CONTEXT.md`', 'Every session', 'Compact project-local context anchor and read-routing guide.'],
952
955
  ['`docs/PROJECT_STATE.md`', 'Every session', 'Current product state and source-of-truth map.'],
953
956
  ['`docs/AGENT_HANDOFF.md`', 'Every session', 'Compact handoff and next recommended step.'],
954
957
  ['`docs/TASK_BOARD.md`', 'Every session', 'Work queue and task status.'],
@@ -1022,6 +1025,20 @@ This project was initialized with the \`${spec.profile}\` HADARA profile.
1022
1025
 
1023
1026
  ${numberedList(sessionStart)}
1024
1027
 
1028
+ ## Required Reading Tiers
1029
+
1030
+ Use semantic tiers to keep session startup compact and deterministic:
1031
+
1032
+ | Tier | Meaning | Default Read Behavior |
1033
+ |---|---|---|
1034
+ | \`current-state\` | Compact docs that establish live project state and route deeper reading, starting with \`.hadara/context/HADARA_CONTEXT.md\`. | Read first at session start or resume. |
1035
+ | \`task-work\` | Active Task Capsule docs, \`docs/TASK_BOARD.md\`, and \`docs/TASK_WORKFLOW_COMMANDS.md\`. | Read when selecting, implementing, finishing, closing, or auditing a task. |
1036
+ | \`conditional-reference\` | Architecture, security, roadmap, validation, release, or project-specific specs. | Read only when the task type, capsule, or Required Reading row condition applies. |
1037
+ | \`historical\` | Completed-task history, older validation records, and previous-state detail. | Never default required reading; read only when investigating history through the handoff Historical Index. |
1038
+ | \`excluded\` | Superseded, archived, local-only, or intentionally non-default material. | Never default required reading unless explicitly reclassified. |
1039
+
1040
+ \`.hadara/context/HADARA_CONTEXT.md\` is the current-state entry point and read-routing guide. Full historical review of \`docs/PROJECT_STATE.md\` is not mandatory every session; rely on compact current-state docs first and follow \`docs/AGENT_HANDOFF.md\` Historical Index only when older history matters. Historical and superseded docs are never default required reading.
1041
+
1025
1042
  ## Required Reading
1026
1043
 
1027
1044
  ${requiredReadingTable}
@@ -1062,6 +1079,22 @@ Prefer tables for repeated records and \`##\`/\`###\` headings for durable secti
1062
1079
  3. Make the smallest coherent change that satisfies acceptance criteria.
1063
1080
  4. Update task-local docs when scope changes.
1064
1081
 
1082
+ ## Documentation Timing and Write Coordination
1083
+
1084
+ Do not defer all documentation until after implementation. Documentation is part of the work, not a post-work report.
1085
+
1086
+ Keep capsule docs current as work changes:
1087
+
1088
+ | Timing | Documents |
1089
+ |---|---|
1090
+ | Before execution | \`PLAN.md\` |
1091
+ | During execution | \`DECISIONS.md\`, \`RISKS.md\`, and \`FILES.md\` |
1092
+ | Immediately after validation | \`TESTS.md\` and \`EVIDENCE.md\` |
1093
+ | Before finish/ready/close | \`ACCEPTANCE.md\`, \`HANDOFF.md\`, and shared state docs |
1094
+ | Before close-source hash is captured | \`docs/TASK_BOARD.md\`, \`docs/PROJECT_STATE.md\`, \`docs/AGENT_HANDOFF.md\`, and roadmap/slice docs when applicable |
1095
+
1096
+ Parallelize read-only discovery, \`rg\`/file inspection, independent validation commands, package or registry metadata inspection, read-only diagnostics, and draft preparation before writes. Serialize same-file writes, evidence append, Task Capsule doc writes, Task Board writes, Project State writes, Agent Handoff writes, before-hash execute operations, \`task finish --execute\`, \`task close --execute\`, and release artifact or publish operations.
1097
+
1065
1098
  ## Standard Task Workflow Loop
1066
1099
 
1067
1100
  The authoritative command semantics live in \`docs/TASK_WORKFLOW_COMMANDS.md\`. For ordinary implementation capsules, use this loop:
@@ -1276,6 +1309,10 @@ function createTaskWorkflowCommandsDoc() {
1276
1309
 
1277
1310
  HADARA task workflow commands are split by responsibility. Similar-looking commands are not interchangeable: some only report state, some check readiness, some perform bounded bookkeeping writes, and some append close evidence.
1278
1311
 
1312
+ ## Required Reading Tier
1313
+
1314
+ \`docs/TASK_WORKFLOW_COMMANDS.md\` is \`task-work\` required reading. Read it when selecting, implementing, finishing, closing, auditing, or changing task workflow commands; do not treat it as a historical archive or a replacement for current-state docs. Start from \`.hadara/context/HADARA_CONTEXT.md\` and compact state docs, then use this document for lifecycle command semantics.
1315
+
1279
1316
  ## Standard Task Loop
1280
1317
 
1281
1318
  Use this loop for ordinary implementation capsules:
@@ -1318,6 +1355,14 @@ The close model has three separate phases: validation proves readiness, close re
1318
1355
 
1319
1356
  Before close, finish all close-source edits: Task Capsule docs, acceptance/tests/handoff notes, evidence summaries, \`docs/TASK_BOARD.md\`, and tracked state docs such as \`docs/PROJECT_STATE.md\`, \`docs/AGENT_HANDOFF.md\`, and roadmap/slice docs when they apply. After \`task close --execute --json\`, changing those documents changes the close source hash and requires rerunning \`task ready\`, \`task close\`, and \`task audit-close\`. Do not paste volatile close evidence ids into close-source docs; prefer stable wording such as "close evidence appended; audit returned closed-valid".
1320
1357
 
1358
+ ## Documentation Timing and Write Coordination
1359
+
1360
+ Do not defer all documentation until after implementation. Keep \`PLAN.md\` current before execution; update \`DECISIONS.md\`, \`RISKS.md\`, and \`FILES.md\` during execution; update \`TESTS.md\` and \`EVIDENCE.md\` immediately after validation; update \`ACCEPTANCE.md\`, \`HANDOFF.md\`, and shared state docs before finish/ready/close; and update shared close-source docs before the close-source hash is captured.
1361
+
1362
+ Parallelize read-only discovery, \`rg\`/file inspection, independent validation commands, package or registry metadata inspection, read-only diagnostics, and draft preparation before writes.
1363
+
1364
+ Serialize same-file writes, evidence append, Task Capsule doc writes, Task Board writes, Project State writes, Agent Handoff writes, before-hash execute operations, \`task finish --execute\`, \`task close --execute\`, and release artifact or publish operations.
1365
+
1321
1366
  ## Command Semantics
1322
1367
 
1323
1368
  | Command | Default Write Behavior | Notes |
@@ -1340,7 +1385,7 @@ Before close, finish all close-source edits: Task Capsule docs, acceptance/tests
1340
1385
  - \`harness validate\` is a direct diagnostic for Task Capsule structure and done-level gates; it is not a replacement for close evidence.
1341
1386
  - \`task complete\` is a read-only workflow compressor. It may report the next lifecycle command, but it must not execute finish, ready, close, or audit commands.
1342
1387
  - \`evidence add-command\` records an operator-supplied command result; it does not run the command. \`--idempotency-key\` is optional; when supplied, same-key repeats return the existing record without appending duplicate Markdown or JSONL rows.
1343
- - \`task finish\` may update only the Task Capsule \`TASK.md\` status and matching \`docs/TASK_BOARD.md\` status/path row.
1388
+ - \`task finish\` may update only the Task Capsule \`TASK.md\` status and the matching \`docs/TASK_BOARD.md\` row's command-owned cells: \`ID\`, \`Title\`, \`Status\`, and \`Capsule\`. It preserves human/mixed-owned \`Notes\` and any extra cells.
1344
1389
  - \`task close\` may append only close evidence. It must not update status docs, Task Board rows, handoff, Project State, roadmap docs, or arbitrary evidence.
1345
1390
  - After \`task close --execute --json\`, close-source document edits intentionally invalidate the previous close proof. Make those edits before close, or rerun ready/close/audit if the edit is unavoidable.
1346
1391
  - \`task audit-close\` is read-only and should be run after \`task close --execute --json\`.
@@ -1352,13 +1397,14 @@ Before close, finish all close-source edits: Task Capsule docs, acceptance/tests
1352
1397
  }
1353
1398
  function createAgentsDoc(spec) {
1354
1399
  const requiredReadingRows = [
1355
- ['1', '`docs/PROJECT_STATE.md`', 'Every session', 'Current product and capability state.'],
1356
- ['2', '`docs/AGENT_HANDOFF.md`', 'Every session', 'Compact continuation state.'],
1357
- ['3', '`docs/TASK_BOARD.md`', 'Every session', 'Current task queue and status.'],
1358
- ['4', '`docs/IMPLEMENTATION_SOP.md`', 'Every session', 'Local workflow and required-reading registry.'],
1359
- ['5', '`docs/TASK_WORKFLOW_COMMANDS.md`', 'Starting, finishing, closing, auditing, or explaining task workflow commands', 'Standard task loop, dry-run boundaries, and command `ok` semantics.']
1400
+ ['1', '`.hadara/context/HADARA_CONTEXT.md`', 'Every session', 'Compact project-local context anchor and read routing.'],
1401
+ ['2', '`docs/PROJECT_STATE.md`', 'Every session', 'Current product and capability state.'],
1402
+ ['3', '`docs/AGENT_HANDOFF.md`', 'Every session', 'Compact continuation state.'],
1403
+ ['4', '`docs/TASK_BOARD.md`', 'Every session', 'Current task queue and status.'],
1404
+ ['5', '`docs/IMPLEMENTATION_SOP.md`', 'Every session', 'Local workflow and required-reading registry.'],
1405
+ ['6', '`docs/TASK_WORKFLOW_COMMANDS.md`', 'Starting, finishing, closing, auditing, or explaining task workflow commands', 'Standard task loop, dry-run boundaries, and command `ok` semantics.']
1360
1406
  ];
1361
- let order = 6;
1407
+ let order = 7;
1362
1408
  if (spec.docs.architecture)
1363
1409
  requiredReadingRows.push([String(order++), '`docs/ARCHITECTURE.md`', 'Architecture, component, or boundary work', 'Current system shape and ownership boundaries.']);
1364
1410
  if (spec.docs.developmentSlices)
@@ -1379,6 +1425,8 @@ function createAgentsDoc(spec) {
1379
1425
  ['Task boundary', 'Keep work inside one Task Capsule whenever possible.', 'Active Task Capsule'],
1380
1426
  ['Task creation', 'If no suitable capsule exists, create one with `hadara task create <title>`.', '`docs/TASK_BOARD.md`'],
1381
1427
  ['Evidence', 'Do not mark work done without evidence. Do not hand-edit `evidence.jsonl`; record failed or blocked checks honestly instead of replacing them with optimistic summaries.', '`EVIDENCE.md`, `evidence.jsonl`'],
1428
+ ['Documentation timing', 'Do not defer all documentation until after implementation; keep capsule docs current as work changes.', 'Task Capsule docs and shared state docs'],
1429
+ ['Write coordination', 'Parallelize read-only discovery and independent validation; serialize evidence append, Task Capsule doc writes, shared state doc writes, before-hash executes, finish/close executes, and release/publish operations.', 'Task Capsule evidence'],
1382
1430
  ['Task workflow', 'For task workflow commands, follow `docs/TASK_WORKFLOW_COMMANDS.md`: record evidence, preview and execute `task finish`, finalize close-source docs, run `task ready`, preview and execute `task close`, then run `task audit-close`.', 'Task Capsule evidence'],
1383
1431
  ['Safety', 'Do not execute dangerous commands without explicit user approval.', 'Task Capsule evidence'],
1384
1432
  ['Secrets', 'Do not write secrets, private logs, or machine-local state into committed files.', 'Changed-file review'],
@@ -1403,6 +1451,20 @@ ${requiredReadingRows.map(formatTableRow).join('\n')}
1403
1451
 
1404
1452
  \`docs/AGENT_HANDOFF.md\` is compact current-state handoff, not full project history. Follow its Historical Index when older completed-task or validation history is needed.
1405
1453
 
1454
+ ## Required Reading Tiers
1455
+
1456
+ Use semantic tiers to keep session startup compact:
1457
+
1458
+ | Tier | Meaning | Default Read Behavior |
1459
+ |---|---|---|
1460
+ | \`current-state\` | Compact docs that establish the live project state and route deeper reading. | Read first at session start or resume. |
1461
+ | \`task-work\` | Active Task Capsule docs and task workflow docs needed to safely perform lifecycle commands. | Read when selecting, implementing, finishing, closing, or auditing a task. |
1462
+ | \`conditional-reference\` | Architecture, security, roadmap, validation, release, or project-specific specs. | Read only when the task type or active capsule references them. |
1463
+ | \`historical\` | Completed-task history, older validation records, and previous-state detail. | Never default required reading; read only when investigating history. |
1464
+ | \`excluded\` | Superseded, archived, local-only, or intentionally non-default material. | Never default required reading unless explicitly reclassified. |
1465
+
1466
+ \`.hadara/context/HADARA_CONTEXT.md\` is the current-state entry point. It should route readers to compact state before task-work or conditional-reference docs. Full historical review of \`docs/PROJECT_STATE.md\` is not mandatory every session; use \`docs/AGENT_HANDOFF.md\` and its Historical Index when older history is needed. Historical and superseded docs are never default required reading.
1467
+
1406
1468
  ## Rules
1407
1469
 
1408
1470
  | Rule | Requirement | Evidence / Update Location |
package/dist/core/fs.js CHANGED
@@ -8,6 +8,11 @@ exports.writeFileIfMissing = writeFileIfMissing;
8
8
  exports.appendLine = appendLine;
9
9
  exports.readTextIfExists = readTextIfExists;
10
10
  exports.writeJsonl = writeJsonl;
11
+ exports.prepareAtomicTextFileWrite = prepareAtomicTextFileWrite;
12
+ exports.commitPreparedAtomicTextFileWrite = commitPreparedAtomicTextFileWrite;
13
+ exports.cleanupPreparedAtomicTextFileWrite = cleanupPreparedAtomicTextFileWrite;
14
+ exports.rollbackPreparedAtomicTextFileWrite = rollbackPreparedAtomicTextFileWrite;
15
+ exports.atomicWriteTextFile = atomicWriteTextFile;
11
16
  exports.slugify = slugify;
12
17
  const node_fs_1 = __importDefault(require("node:fs"));
13
18
  const node_path_1 = __importDefault(require("node:path"));
@@ -33,6 +38,63 @@ function readTextIfExists(filePath) {
33
38
  function writeJsonl(filePath, event) {
34
39
  appendLine(filePath, JSON.stringify(event));
35
40
  }
41
+ function prepareAtomicTextFileWrite(projectRoot, relativePath, content) {
42
+ const targetPath = resolveProjectRelativeWritePath(projectRoot, relativePath);
43
+ ensureDir(node_path_1.default.dirname(targetPath));
44
+ const previousExists = node_fs_1.default.existsSync(targetPath);
45
+ const previousContent = previousExists ? node_fs_1.default.readFileSync(targetPath, 'utf8') : '';
46
+ const tempPath = uniqueTempPath(targetPath, 'write');
47
+ node_fs_1.default.writeFileSync(tempPath, content, { encoding: 'utf8', flag: 'wx' });
48
+ return { relativePath, targetPath, tempPath, content, previousExists, previousContent };
49
+ }
50
+ function commitPreparedAtomicTextFileWrite(write) {
51
+ node_fs_1.default.renameSync(write.tempPath, write.targetPath);
52
+ }
53
+ function cleanupPreparedAtomicTextFileWrite(write) {
54
+ if (node_fs_1.default.existsSync(write.tempPath))
55
+ node_fs_1.default.rmSync(write.tempPath, { force: true });
56
+ }
57
+ function rollbackPreparedAtomicTextFileWrite(write) {
58
+ cleanupPreparedAtomicTextFileWrite(write);
59
+ if (write.previousExists) {
60
+ const rollbackTempPath = uniqueTempPath(write.targetPath, 'rollback');
61
+ node_fs_1.default.writeFileSync(rollbackTempPath, write.previousContent, { encoding: 'utf8', flag: 'wx' });
62
+ node_fs_1.default.renameSync(rollbackTempPath, write.targetPath);
63
+ }
64
+ else if (node_fs_1.default.existsSync(write.targetPath)) {
65
+ node_fs_1.default.rmSync(write.targetPath, { force: true });
66
+ }
67
+ }
68
+ function atomicWriteTextFile(projectRoot, relativePath, content) {
69
+ const prepared = prepareAtomicTextFileWrite(projectRoot, relativePath, content);
70
+ try {
71
+ commitPreparedAtomicTextFileWrite(prepared);
72
+ }
73
+ catch (error) {
74
+ cleanupPreparedAtomicTextFileWrite(prepared);
75
+ throw error;
76
+ }
77
+ }
78
+ function uniqueTempPath(targetPath, purpose) {
79
+ const dir = node_path_1.default.dirname(targetPath);
80
+ const base = node_path_1.default.basename(targetPath);
81
+ for (let attempt = 0; attempt < 100; attempt += 1) {
82
+ const suffix = `${process.pid}-${Date.now()}-${attempt}-${Math.random().toString(16).slice(2)}`;
83
+ const candidate = node_path_1.default.join(dir, `.hadara-atomic-${purpose}-${suffix}-${base}`);
84
+ if (!node_fs_1.default.existsSync(candidate))
85
+ return candidate;
86
+ }
87
+ throw new Error(`Could not allocate temporary path for ${targetPath}`);
88
+ }
89
+ function resolveProjectRelativeWritePath(projectRoot, relativePath) {
90
+ const rootPath = node_path_1.default.resolve(projectRoot);
91
+ const targetPath = node_path_1.default.resolve(rootPath, relativePath);
92
+ const relative = node_path_1.default.relative(rootPath, targetPath);
93
+ if (relative.startsWith('..') || node_path_1.default.isAbsolute(relative)) {
94
+ throw new Error(`Refusing to write outside project: ${relativePath}`);
95
+ }
96
+ return targetPath;
97
+ }
36
98
  function slugify(input) {
37
99
  return input
38
100
  .normalize('NFKD')
@@ -30,6 +30,7 @@ const evidence_list_schema_json_1 = __importDefault(require("../schemas/evidence
30
30
  const evidence_migration_preview_schema_json_1 = __importDefault(require("../schemas/evidence-migration-preview.schema.json"));
31
31
  const event_schema_json_1 = __importDefault(require("../schemas/event.schema.json"));
32
32
  const feature_smoke_schema_json_1 = __importDefault(require("../schemas/feature-smoke.schema.json"));
33
+ const harness_validate_schema_json_1 = __importDefault(require("../schemas/harness-validate.schema.json"));
33
34
  const handoff_suggestion_schema_json_1 = __importDefault(require("../schemas/handoff-suggestion.schema.json"));
34
35
  const install_plan_schema_json_1 = __importDefault(require("../schemas/install-plan.schema.json"));
35
36
  const next_action_schema_json_1 = __importDefault(require("../schemas/next-action.schema.json"));
@@ -95,6 +96,7 @@ const registeredSchemas = {
95
96
  'hadara.evidence.migration_preview.v1': evidence_migration_preview_schema_json_1.default,
96
97
  'hadara.event.v1': event_schema_json_1.default,
97
98
  'hadara.featureSmoke.v1': feature_smoke_schema_json_1.default,
99
+ 'hadara.harness.validate.v1': harness_validate_schema_json_1.default,
98
100
  'hadara.handoff.suggestion.v1': handoff_suggestion_schema_json_1.default,
99
101
  'hadara.install.plan.v1': install_plan_schema_json_1.default,
100
102
  'hadara.next_action.v1': next_action_schema_json_1.default,