@lamentis/naome 1.1.2 → 1.2.1

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 (204) hide show
  1. package/Cargo.lock +2 -2
  2. package/Cargo.toml +1 -1
  3. package/LICENSE +180 -21
  4. package/README.md +49 -6
  5. package/bin/naome-node.js +2 -1579
  6. package/bin/naome.js +68 -16
  7. package/crates/naome-cli/Cargo.toml +1 -1
  8. package/crates/naome-cli/src/check_commands.rs +135 -0
  9. package/crates/naome-cli/src/cli_args.rs +5 -0
  10. package/crates/naome-cli/src/dispatcher.rs +37 -0
  11. package/crates/naome-cli/src/install_bridge.rs +83 -0
  12. package/crates/naome-cli/src/main.rs +60 -341
  13. package/crates/naome-cli/src/prompt_commands.rs +68 -0
  14. package/crates/naome-cli/src/quality_commands.rs +229 -0
  15. package/crates/naome-cli/src/simple_commands.rs +53 -0
  16. package/crates/naome-cli/src/workflow_commands.rs +153 -0
  17. package/crates/naome-core/Cargo.toml +1 -1
  18. package/crates/naome-core/src/decision/checks.rs +64 -0
  19. package/crates/naome-core/src/decision/idle.rs +67 -0
  20. package/crates/naome-core/src/decision/json.rs +36 -0
  21. package/crates/naome-core/src/decision/states.rs +165 -0
  22. package/crates/naome-core/src/decision.rs +131 -353
  23. package/crates/naome-core/src/harness_health/integrity.rs +96 -0
  24. package/crates/naome-core/src/harness_health.rs +14 -126
  25. package/crates/naome-core/src/install_plan.rs +5 -0
  26. package/crates/naome-core/src/intent/classifier.rs +171 -0
  27. package/crates/naome-core/src/intent/envelope.rs +108 -0
  28. package/crates/naome-core/src/intent/legacy.rs +138 -0
  29. package/crates/naome-core/src/intent/legacy_response.rs +76 -0
  30. package/crates/naome-core/src/intent/model.rs +71 -0
  31. package/crates/naome-core/src/intent/patterns.rs +170 -0
  32. package/crates/naome-core/src/intent/resolver.rs +162 -0
  33. package/crates/naome-core/src/intent/resolver_active.rs +17 -0
  34. package/crates/naome-core/src/intent/resolver_baseline.rs +55 -0
  35. package/crates/naome-core/src/intent/resolver_catalog.rs +167 -0
  36. package/crates/naome-core/src/intent/resolver_policy.rs +72 -0
  37. package/crates/naome-core/src/intent/resolver_shared.rs +55 -0
  38. package/crates/naome-core/src/intent/risk.rs +40 -0
  39. package/crates/naome-core/src/intent/segment.rs +170 -0
  40. package/crates/naome-core/src/intent.rs +64 -879
  41. package/crates/naome-core/src/journal.rs +9 -20
  42. package/crates/naome-core/src/lib.rs +15 -0
  43. package/crates/naome-core/src/paths.rs +3 -1
  44. package/crates/naome-core/src/quality/adapter_support.rs +89 -0
  45. package/crates/naome-core/src/quality/adapters.rs +131 -0
  46. package/crates/naome-core/src/quality/baseline.rs +75 -0
  47. package/crates/naome-core/src/quality/checks/duplicate_blocks.rs +175 -0
  48. package/crates/naome-core/src/quality/checks/near_duplicates.rs +130 -0
  49. package/crates/naome-core/src/quality/checks.rs +228 -0
  50. package/crates/naome-core/src/quality/cleanup.rs +84 -0
  51. package/crates/naome-core/src/quality/config.rs +102 -0
  52. package/crates/naome-core/src/quality/config_support.rs +24 -0
  53. package/crates/naome-core/src/quality/mod.rs +108 -0
  54. package/crates/naome-core/src/quality/scanner/repo_paths.rs +103 -0
  55. package/crates/naome-core/src/quality/scanner.rs +379 -0
  56. package/crates/naome-core/src/quality/structure/adapters.rs +84 -0
  57. package/crates/naome-core/src/quality/structure/checks/basic.rs +153 -0
  58. package/crates/naome-core/src/quality/structure/checks/directory.rs +144 -0
  59. package/crates/naome-core/src/quality/structure/checks/pairing.rs +63 -0
  60. package/crates/naome-core/src/quality/structure/checks.rs +124 -0
  61. package/crates/naome-core/src/quality/structure/classify/roles.rs +188 -0
  62. package/crates/naome-core/src/quality/structure/classify.rs +94 -0
  63. package/crates/naome-core/src/quality/structure/config.rs +89 -0
  64. package/crates/naome-core/src/quality/structure/defaults.rs +107 -0
  65. package/crates/naome-core/src/quality/structure/mod.rs +77 -0
  66. package/crates/naome-core/src/quality/structure/model.rs +124 -0
  67. package/crates/naome-core/src/quality/types.rs +292 -0
  68. package/crates/naome-core/src/route/builtin_checks.rs +155 -0
  69. package/crates/naome-core/src/route/builtin_context.rs +73 -0
  70. package/crates/naome-core/src/route/builtin_integrity.rs +49 -0
  71. package/crates/naome-core/src/route/builtin_require.rs +40 -0
  72. package/crates/naome-core/src/route/context.rs +180 -0
  73. package/crates/naome-core/src/route/execution.rs +96 -0
  74. package/crates/naome-core/src/route/execution_baselines.rs +146 -0
  75. package/crates/naome-core/src/route/execution_support.rs +57 -0
  76. package/crates/naome-core/src/route/execution_tasks.rs +71 -0
  77. package/crates/naome-core/src/route/git_ops.rs +72 -0
  78. package/crates/naome-core/src/route/quality_gate.rs +73 -0
  79. package/crates/naome-core/src/route/quality_gate_config.rs +126 -0
  80. package/crates/naome-core/src/route/quality_gate_snapshot.rs +69 -0
  81. package/crates/naome-core/src/route/worktree.rs +75 -0
  82. package/crates/naome-core/src/route/worktree_files.rs +32 -0
  83. package/crates/naome-core/src/route/worktree_plan.rs +131 -0
  84. package/crates/naome-core/src/route.rs +44 -1155
  85. package/crates/naome-core/src/task_state/admission.rs +63 -0
  86. package/crates/naome-core/src/task_state/admission_proof.rs +72 -0
  87. package/crates/naome-core/src/task_state/api.rs +130 -0
  88. package/crates/naome-core/src/task_state/commit_gate.rs +138 -0
  89. package/crates/naome-core/src/task_state/compact_proof.rs +160 -0
  90. package/crates/naome-core/src/task_state/completed_refresh.rs +89 -0
  91. package/crates/naome-core/src/task_state/completion.rs +72 -0
  92. package/crates/naome-core/src/task_state/deleted_paths.rs +47 -0
  93. package/crates/naome-core/src/task_state/diff.rs +95 -0
  94. package/crates/naome-core/src/task_state/evidence.rs +154 -0
  95. package/crates/naome-core/src/task_state/git_io.rs +86 -0
  96. package/crates/naome-core/src/task_state/git_parse.rs +86 -0
  97. package/crates/naome-core/src/task_state/git_refs.rs +37 -0
  98. package/crates/naome-core/src/task_state/human_review_state.rs +31 -0
  99. package/crates/naome-core/src/task_state/mod.rs +38 -0
  100. package/crates/naome-core/src/task_state/process_guard.rs +40 -0
  101. package/crates/naome-core/src/task_state/progress.rs +123 -0
  102. package/crates/naome-core/src/task_state/proof.rs +139 -0
  103. package/crates/naome-core/src/task_state/proof_entry.rs +66 -0
  104. package/crates/naome-core/src/task_state/proof_model.rs +70 -0
  105. package/crates/naome-core/src/task_state/proof_sources.rs +76 -0
  106. package/crates/naome-core/src/task_state/push_gate.rs +49 -0
  107. package/crates/naome-core/src/task_state/reconcile.rs +7 -0
  108. package/crates/naome-core/src/task_state/repair.rs +168 -0
  109. package/crates/naome-core/src/task_state/shape.rs +117 -0
  110. package/crates/naome-core/src/task_state/task_diff_api.rs +170 -0
  111. package/crates/naome-core/src/task_state/task_records.rs +131 -0
  112. package/crates/naome-core/src/task_state/task_references.rs +126 -0
  113. package/crates/naome-core/src/task_state/types.rs +87 -0
  114. package/crates/naome-core/src/task_state/util.rs +137 -0
  115. package/crates/naome-core/src/verification/render.rs +122 -0
  116. package/crates/naome-core/src/verification.rs +177 -58
  117. package/crates/naome-core/src/verification_contract.rs +49 -21
  118. package/crates/naome-core/src/workflow/integrity.rs +123 -0
  119. package/crates/naome-core/src/workflow/integrity_normalize.rs +7 -0
  120. package/crates/naome-core/src/workflow/integrity_support.rs +110 -0
  121. package/crates/naome-core/src/workflow/mod.rs +18 -0
  122. package/crates/naome-core/src/workflow/mutation.rs +68 -0
  123. package/crates/naome-core/src/workflow/output.rs +111 -0
  124. package/crates/naome-core/src/workflow/phase_inference.rs +73 -0
  125. package/crates/naome-core/src/workflow/phases.rs +169 -0
  126. package/crates/naome-core/src/workflow/policy.rs +156 -0
  127. package/crates/naome-core/src/workflow/processes.rs +91 -0
  128. package/crates/naome-core/src/workflow/types.rs +42 -0
  129. package/crates/naome-core/tests/decision.rs +24 -118
  130. package/crates/naome-core/tests/harness_health.rs +5 -0
  131. package/crates/naome-core/tests/intent.rs +97 -792
  132. package/crates/naome-core/tests/intent_support/mod.rs +133 -0
  133. package/crates/naome-core/tests/intent_v2.rs +90 -0
  134. package/crates/naome-core/tests/quality.rs +319 -0
  135. package/crates/naome-core/tests/quality_structure.rs +116 -0
  136. package/crates/naome-core/tests/quality_structure_adapters.rs +98 -0
  137. package/crates/naome-core/tests/quality_structure_policy.rs +125 -0
  138. package/crates/naome-core/tests/quality_structure_support/mod.rs +249 -0
  139. package/crates/naome-core/tests/repo_support/mod.rs +16 -0
  140. package/crates/naome-core/tests/repo_support/repo.rs +113 -0
  141. package/crates/naome-core/tests/repo_support/repo_factories.rs +99 -0
  142. package/crates/naome-core/tests/repo_support/repo_helpers.rs +123 -0
  143. package/crates/naome-core/tests/repo_support/routes.rs +81 -0
  144. package/crates/naome-core/tests/repo_support/verification.rs +168 -0
  145. package/crates/naome-core/tests/repo_support/verification_values.rs +135 -0
  146. package/crates/naome-core/tests/route.rs +1 -1476
  147. package/crates/naome-core/tests/route_baseline.rs +86 -0
  148. package/crates/naome-core/tests/route_completion.rs +141 -0
  149. package/crates/naome-core/tests/route_harness_refresh.rs +135 -0
  150. package/crates/naome-core/tests/route_user_diff.rs +198 -0
  151. package/crates/naome-core/tests/route_worktree.rs +54 -0
  152. package/crates/naome-core/tests/task_state.rs +60 -429
  153. package/crates/naome-core/tests/task_state_compact.rs +110 -0
  154. package/crates/naome-core/tests/task_state_compact_support/mod.rs +5 -0
  155. package/crates/naome-core/tests/task_state_compact_support/repo.rs +130 -0
  156. package/crates/naome-core/tests/task_state_compact_support/states.rs +151 -0
  157. package/crates/naome-core/tests/task_state_support/mod.rs +163 -0
  158. package/crates/naome-core/tests/task_state_support/states.rs +84 -0
  159. package/crates/naome-core/tests/verification.rs +4 -45
  160. package/crates/naome-core/tests/verification_contract.rs +22 -78
  161. package/crates/naome-core/tests/workflow_integrity.rs +85 -0
  162. package/crates/naome-core/tests/workflow_policy.rs +139 -0
  163. package/crates/naome-core/tests/workflow_support/mod.rs +194 -0
  164. package/installer/agents.js +90 -0
  165. package/installer/context.js +67 -0
  166. package/installer/filesystem.js +166 -0
  167. package/installer/flows.js +84 -0
  168. package/installer/git-boundary.js +170 -0
  169. package/installer/git-hook-content.js +36 -0
  170. package/installer/git-hooks.js +134 -0
  171. package/installer/git-local.js +2 -0
  172. package/installer/git-shared.js +35 -0
  173. package/installer/harness-file-ops.js +140 -0
  174. package/installer/harness-files.js +56 -0
  175. package/installer/harness-verification.js +123 -0
  176. package/installer/install-plan.js +66 -0
  177. package/installer/main.js +25 -0
  178. package/installer/manifest-state.js +167 -0
  179. package/installer/native-build.js +24 -0
  180. package/installer/native-format.js +6 -0
  181. package/installer/native.js +162 -0
  182. package/installer/output.js +131 -0
  183. package/installer/version.js +32 -0
  184. package/native/darwin-arm64/naome +0 -0
  185. package/native/linux-x64/naome +0 -0
  186. package/package.json +3 -2
  187. package/templates/naome-root/.naome/bin/check-harness-health.js +66 -85
  188. package/templates/naome-root/.naome/bin/check-task-state.js +9 -10
  189. package/templates/naome-root/.naome/bin/naome.js +51 -76
  190. package/templates/naome-root/.naome/manifest.json +22 -18
  191. package/templates/naome-root/.naome/repository-quality-baseline.json +5 -0
  192. package/templates/naome-root/.naome/repository-quality.json +24 -0
  193. package/templates/naome-root/.naome/repository-structure.json +90 -0
  194. package/templates/naome-root/.naome/task-contract.schema.json +93 -11
  195. package/templates/naome-root/.naome/upgrade-state.json +1 -1
  196. package/templates/naome-root/.naome/verification.json +38 -0
  197. package/templates/naome-root/AGENTS.md +3 -0
  198. package/templates/naome-root/docs/naome/agent-workflow.md +25 -12
  199. package/templates/naome-root/docs/naome/execution.md +25 -21
  200. package/templates/naome-root/docs/naome/index.md +5 -3
  201. package/templates/naome-root/docs/naome/repository-quality.md +46 -0
  202. package/templates/naome-root/docs/naome/repository-structure.md +51 -0
  203. package/templates/naome-root/docs/naome/testing.md +13 -0
  204. package/crates/naome-core/src/task_state.rs +0 -2210
@@ -1,43 +1,16 @@
1
- use std::collections::HashMap;
1
+ mod task_state_support;
2
+
2
3
  use std::fs;
3
- use std::path::{Path, PathBuf};
4
- use std::process::Command;
5
- use std::sync::atomic::{AtomicU64, Ordering};
6
- use std::time::{SystemTime, UNIX_EPOCH};
7
-
8
- use naome_core::{validate_task_state, HarnessHealthOptions, TaskStateMode, TaskStateOptions};
9
- use serde_json::{json, Value};
10
- use sha2::{Digest, Sha256};
11
-
12
- static FIXTURE_COUNTER: AtomicU64 = AtomicU64::new(0);
13
-
14
- const MACHINE_OWNED_PATHS: &[&str] = &[
15
- "AGENTS.md",
16
- ".naome/package.json",
17
- ".naome/bin/naome.js",
18
- ".naome/bin/check-task-state.js",
19
- ".naome/bin/check-harness-health.js",
20
- ".naome/task-contract.schema.json",
21
- "docs/naome/index.md",
22
- "docs/naome/first-run.md",
23
- "docs/naome/agent-workflow.md",
24
- "docs/naome/execution.md",
25
- "docs/naome/upgrade.md",
26
- ];
27
-
28
- const PROJECT_OWNED_PATHS: &[&str] = &[
29
- ".naomeignore",
30
- ".naome/init-state.json",
31
- ".naome/manifest.json",
32
- ".naome/task-state.json",
33
- ".naome/upgrade-state.json",
34
- ".naome/verification.json",
35
- "docs/naome/architecture.md",
36
- "docs/naome/decisions.md",
37
- "docs/naome/repo-profile.md",
38
- "docs/naome/security.md",
39
- "docs/naome/testing.md",
40
- ];
4
+
5
+ use naome_core::{
6
+ validate_task_state, TaskStateMode, TaskStateOptions, TaskStateReport, MACHINE_OWNED_PATHS,
7
+ PROJECT_OWNED_PATHS,
8
+ };
9
+ use serde_json::json;
10
+ use task_state_support::{
11
+ active_task, complete_task_state, idle_task_state, strict_health_options, successful_admission,
12
+ successful_proof, TaskFixture,
13
+ };
41
14
 
42
15
  #[test]
43
16
  fn accepts_idle_task_state_template() {
@@ -175,14 +148,7 @@ fn commit_gate_allows_install_baseline_with_gitignore_and_project_owned_paths()
175
148
  }),
176
149
  );
177
150
 
178
- let report = validate_task_state(
179
- repo.path(),
180
- TaskStateOptions {
181
- mode: TaskStateMode::CommitGate,
182
- ..TaskStateOptions::default()
183
- },
184
- )
185
- .unwrap();
151
+ let report = commit_gate_report(&repo);
186
152
 
187
153
  assert!(report.errors.is_empty(), "{:#?}", report.errors);
188
154
  }
@@ -197,27 +163,10 @@ fn commit_gate_allows_staged_harness_refresh_split_from_completed_task() {
197
163
  repo.init_git();
198
164
  repo.write("README.md", "# Task result\n");
199
165
  repo.write("AGENTS.md", "# Agent Instructions\n\nUpdated by sync.\n");
200
- repo.write_json(
201
- ".naome/manifest.json",
202
- json!({
203
- "name": "naome",
204
- "harnessVersion": "1.1.0",
205
- "profile": "standard",
206
- "machineOwned": MACHINE_OWNED_PATHS,
207
- "projectOwned": PROJECT_OWNED_PATHS,
208
- "integrity": {}
209
- }),
210
- );
166
+ write_harness_manifest(&repo, "1.1.0");
211
167
  repo.git(["add", "AGENTS.md", ".naome/manifest.json"]);
212
168
 
213
- let report = validate_task_state(
214
- repo.path(),
215
- TaskStateOptions {
216
- mode: TaskStateMode::CommitGate,
217
- ..TaskStateOptions::default()
218
- },
219
- )
220
- .unwrap();
169
+ let report = commit_gate_report(&repo);
221
170
 
222
171
  assert!(report.errors.is_empty(), "{:#?}", report.errors);
223
172
  }
@@ -232,27 +181,10 @@ fn commit_gate_allows_staged_harness_refresh_after_completed_task_is_baselined()
232
181
  repo.write("README.md", "# Completed task result\n");
233
182
  repo.init_git();
234
183
  repo.write("AGENTS.md", "# Agent Instructions\n\nUpdated by sync.\n");
235
- repo.write_json(
236
- ".naome/manifest.json",
237
- json!({
238
- "name": "naome",
239
- "harnessVersion": "1.1.1",
240
- "profile": "standard",
241
- "machineOwned": MACHINE_OWNED_PATHS,
242
- "projectOwned": PROJECT_OWNED_PATHS,
243
- "integrity": {}
244
- }),
245
- );
184
+ write_harness_manifest(&repo, "1.1.1");
246
185
  repo.git(["add", "AGENTS.md", ".naome/manifest.json"]);
247
186
 
248
- let report = validate_task_state(
249
- repo.path(),
250
- TaskStateOptions {
251
- mode: TaskStateMode::CommitGate,
252
- ..TaskStateOptions::default()
253
- },
254
- )
255
- .unwrap();
187
+ let report = commit_gate_report(&repo);
256
188
 
257
189
  assert!(report.errors.is_empty(), "{:#?}", report.errors);
258
190
  }
@@ -269,14 +201,7 @@ fn commit_gate_rejects_harness_refresh_when_completed_task_proof_is_missing() {
269
201
  repo.write("AGENTS.md", "# Agent Instructions\n\nUpdated by sync.\n");
270
202
  repo.git(["add", "AGENTS.md"]);
271
203
 
272
- let report = validate_task_state(
273
- repo.path(),
274
- TaskStateOptions {
275
- mode: TaskStateMode::CommitGate,
276
- ..TaskStateOptions::default()
277
- },
278
- )
279
- .unwrap();
204
+ let report = commit_gate_report(&repo);
280
205
 
281
206
  assert!(
282
207
  report
@@ -310,14 +235,7 @@ fn commit_gate_rejects_harness_refresh_with_pending_upgrade_state() {
310
235
  );
311
236
  repo.git(["add", "AGENTS.md", ".naome/upgrade-state.json"]);
312
237
 
313
- let report = validate_task_state(
314
- repo.path(),
315
- TaskStateOptions {
316
- mode: TaskStateMode::CommitGate,
317
- ..TaskStateOptions::default()
318
- },
319
- )
320
- .unwrap();
238
+ let report = commit_gate_report(&repo);
321
239
 
322
240
  assert!(
323
241
  report
@@ -350,14 +268,7 @@ fn commit_gate_rejects_support_only_harness_refresh_after_completed_task_is_base
350
268
  );
351
269
  repo.git(["add", ".naome/upgrade-state.json"]);
352
270
 
353
- let report = validate_task_state(
354
- repo.path(),
355
- TaskStateOptions {
356
- mode: TaskStateMode::CommitGate,
357
- ..TaskStateOptions::default()
358
- },
359
- )
360
- .unwrap();
271
+ let report = commit_gate_report(&repo);
361
272
 
362
273
  assert!(!report.errors.is_empty(), "{:#?}", report.errors);
363
274
  }
@@ -374,70 +285,29 @@ fn commit_gate_ignores_unstaged_user_edits_outside_completed_task_scope() {
374
285
  repo.write("USER.md", "user local edit\n");
375
286
  repo.git(["add", "README.md", ".naome/task-state.json"]);
376
287
 
377
- let report = validate_task_state(
378
- repo.path(),
379
- TaskStateOptions {
380
- mode: TaskStateMode::CommitGate,
381
- ..TaskStateOptions::default()
382
- },
383
- )
384
- .unwrap();
288
+ let report = commit_gate_report(&repo);
385
289
 
386
290
  assert!(report.errors.is_empty(), "{:#?}", report.errors);
387
291
  }
388
292
 
389
293
  #[test]
390
294
  fn progress_accepts_implementing_task_with_scoped_diff_without_proof() {
391
- let repo = TaskFixture::new(json!({
392
- "schema": "naome.task-state.v1",
393
- "version": 1,
394
- "status": "implementing",
395
- "activeTask": active_task(json!({
396
- "allowedPaths": ["README.md"],
397
- "proofResults": []
398
- })),
399
- "blocker": null,
400
- "updatedAt": "2026-05-04T12:00:00.000Z"
401
- }));
295
+ let repo = TaskFixture::new(implementing_readme_task());
402
296
  repo.init_git();
403
297
  repo.write("README.md", "# In progress\n");
404
298
 
405
- let report = validate_task_state(
406
- repo.path(),
407
- TaskStateOptions {
408
- mode: TaskStateMode::Progress,
409
- ..TaskStateOptions::default()
410
- },
411
- )
412
- .unwrap();
299
+ let report = progress_report(&repo);
413
300
 
414
301
  assert!(report.errors.is_empty(), "{:#?}", report.errors);
415
302
  }
416
303
 
417
304
  #[test]
418
305
  fn progress_rejects_in_flight_work_outside_scope() {
419
- let repo = TaskFixture::new(json!({
420
- "schema": "naome.task-state.v1",
421
- "version": 1,
422
- "status": "implementing",
423
- "activeTask": active_task(json!({
424
- "allowedPaths": ["README.md"],
425
- "proofResults": []
426
- })),
427
- "blocker": null,
428
- "updatedAt": "2026-05-04T12:00:00.000Z"
429
- }));
306
+ let repo = TaskFixture::new(implementing_readme_task());
430
307
  repo.init_git();
431
308
  repo.write("src/session.js", "export const session = null;\n");
432
309
 
433
- let report = validate_task_state(
434
- repo.path(),
435
- TaskStateOptions {
436
- mode: TaskStateMode::Progress,
437
- ..TaskStateOptions::default()
438
- },
439
- )
440
- .unwrap();
310
+ let report = progress_report(&repo);
441
311
  let joined = report.errors.join("\n");
442
312
 
443
313
  assert!(joined.contains("Changed files outside allowedPaths"));
@@ -497,291 +367,52 @@ fn accepts_committed_deleted_file_evidence_after_task_baseline_is_clean() {
497
367
  assert!(report.errors.is_empty(), "{:#?}", report.errors);
498
368
  }
499
369
 
500
- struct TaskFixture {
501
- root: PathBuf,
502
- }
503
-
504
- impl TaskFixture {
505
- fn new(task_state: Value) -> Self {
506
- let nonce = SystemTime::now()
507
- .duration_since(UNIX_EPOCH)
508
- .unwrap()
509
- .as_nanos();
510
- let counter = FIXTURE_COUNTER.fetch_add(1, Ordering::Relaxed);
511
- let root = std::env::temp_dir().join(format!(
512
- "naome-task-state-rust-{}-{nonce}-{counter}",
513
- std::process::id()
514
- ));
515
- fs::create_dir_all(root.join(".naome")).unwrap();
516
- write_json(&root, ".naome/task-state.json", task_state);
517
- write_json(
518
- &root,
519
- ".naome/init-state.json",
520
- json!({
521
- "initialized": true,
522
- "intakeStatus": "complete",
523
- "initializedAt": "2026-05-04T12:00:00.000Z",
524
- "initializedBy": "naome-test",
525
- "blockedReason": null,
526
- "requiredDocs": []
527
- }),
528
- );
529
- write_json(
530
- &root,
531
- ".naome/upgrade-state.json",
532
- json!({
533
- "status": "complete",
534
- "fromVersion": null,
535
- "toVersion": "0.6.1",
536
- "pending": [],
537
- "completed": []
538
- }),
539
- );
540
- write_json(
541
- &root,
542
- ".naome/verification.json",
543
- json!({
544
- "schema": "naome.verification.v1",
545
- "version": 1,
546
- "status": "ready",
547
- "lastUpdated": "2026-05-04",
548
- "checks": [
549
- {
550
- "id": "diff-check",
551
- "command": "git diff --check",
552
- "cwd": ".",
553
- "purpose": "Detect whitespace and patch formatting issues.",
554
- "cost": "fast",
555
- "source": "git",
556
- "evidence": ["README.md"],
557
- "lastVerified": "2026-05-04"
558
- }
559
- ],
560
- "changeTypes": [],
561
- "releaseGates": []
562
- }),
563
- );
564
- Self { root }
565
- }
566
-
567
- fn path(&self) -> &Path {
568
- &self.root
569
- }
570
-
571
- fn write(&self, relative_path: &str, content: &str) {
572
- let path = self.root.join(relative_path);
573
- fs::create_dir_all(path.parent().unwrap()).unwrap();
574
- fs::write(path, content).unwrap();
575
- }
576
-
577
- fn write_json(&self, relative_path: &str, value: Value) {
578
- write_json(&self.root, relative_path, value);
579
- }
580
-
581
- fn install_healthy_harness(&self) -> HashMap<String, String> {
582
- let mut integrity = HashMap::new();
583
-
584
- for relative_path in MACHINE_OWNED_PATHS {
585
- let content = machine_content(relative_path);
586
- self.write(relative_path, &content);
587
- integrity.insert(
588
- (*relative_path).to_string(),
589
- format!("sha256:{}", sha256(&content)),
590
- );
591
- }
592
-
593
- for relative_path in PROJECT_OWNED_PATHS {
594
- let path = self.root.join(relative_path);
595
- if *relative_path == ".naome/manifest.json" || path.exists() {
596
- continue;
597
- }
598
- let content = if *relative_path == ".naomeignore" {
599
- ".naome/archive/\n".to_string()
600
- } else {
601
- format!("# {relative_path}\n")
602
- };
603
- self.write(relative_path, &content);
604
- }
605
-
606
- fs::create_dir_all(self.root.join(".naome/archive")).unwrap();
607
- self.write_json(
608
- ".naome/manifest.json",
609
- json!({
610
- "name": "naome",
611
- "harnessVersion": "0.6.1",
612
- "profile": "standard",
613
- "installedAt": "2026-05-05T00:00:00.000Z",
614
- "machineOwned": MACHINE_OWNED_PATHS,
615
- "projectOwned": PROJECT_OWNED_PATHS,
616
- "integrity": integrity
617
- }),
618
- );
619
-
620
- integrity
621
- }
622
-
623
- fn init_git(&self) {
624
- self.git(["init"]);
625
- self.git(["config", "user.email", "naome@example.com"]);
626
- self.git(["config", "user.name", "NAOME Test"]);
627
- self.git(["add", "."]);
628
- self.git(["commit", "-m", "baseline"]);
629
- self.refresh_admission_head();
630
- }
631
-
632
- fn refresh_admission_head(&self) {
633
- let path = self.root.join(".naome/task-state.json");
634
- let mut task_state: Value =
635
- serde_json::from_str(&fs::read_to_string(&path).unwrap()).unwrap();
636
- let Some(active_task) = task_state.get_mut("activeTask") else {
637
- return;
638
- };
639
- let Some(admission) = active_task.get_mut("admission") else {
640
- return;
641
- };
642
-
643
- admission["gitHead"] = Value::String(self.git(["rev-parse", "HEAD"]).trim().to_string());
644
- fs::write(
645
- path,
646
- format!("{}\n", serde_json::to_string_pretty(&task_state).unwrap()),
647
- )
648
- .unwrap();
649
- }
650
-
651
- fn git<const N: usize>(&self, args: [&str; N]) -> String {
652
- let output = Command::new("git")
653
- .args(args)
654
- .current_dir(&self.root)
655
- .output()
656
- .unwrap();
657
- assert!(
658
- output.status.success(),
659
- "{}\n{}",
660
- String::from_utf8_lossy(&output.stdout),
661
- String::from_utf8_lossy(&output.stderr)
662
- );
663
- String::from_utf8_lossy(&output.stdout).to_string()
664
- }
665
- }
666
-
667
- fn strict_health_options(expected_integrity: HashMap<String, String>) -> HarnessHealthOptions {
668
- HarnessHealthOptions {
669
- expected_integrity,
670
- allow_missing_integrity: false,
671
- allow_missing_archive: false,
672
- }
673
- }
674
-
675
- fn machine_content(relative_path: &str) -> String {
676
- match relative_path {
677
- "AGENTS.md" => [
678
- "# Agent Instructions",
679
- "Run node .naome/bin/check-harness-health.js before feature work.",
680
- "Run node .naome/bin/check-task-state.js --admission before task work.",
681
- "",
682
- ]
683
- .join("\n"),
684
- ".naome/package.json" => format!("{}\n", json!({ "type": "commonjs" })),
685
- _ => format!("# {relative_path}\n"),
686
- }
687
- }
688
-
689
- fn sha256(content: &str) -> String {
690
- let mut hasher = Sha256::new();
691
- hasher.update(content.as_bytes());
692
- format!("{:x}", hasher.finalize())
370
+ fn write_harness_manifest(repo: &TaskFixture, harness_version: &str) {
371
+ repo.write_json(
372
+ ".naome/manifest.json",
373
+ json!({
374
+ "name": "naome",
375
+ "harnessVersion": harness_version,
376
+ "profile": "standard",
377
+ "machineOwned": MACHINE_OWNED_PATHS,
378
+ "projectOwned": PROJECT_OWNED_PATHS,
379
+ "integrity": {}
380
+ }),
381
+ );
693
382
  }
694
383
 
695
- fn write_json(root: &Path, relative_path: &str, value: Value) {
696
- let path = root.join(relative_path);
697
- fs::create_dir_all(path.parent().unwrap()).unwrap();
698
- fs::write(
699
- path,
700
- format!("{}\n", serde_json::to_string_pretty(&value).unwrap()),
384
+ fn commit_gate_report(repo: &TaskFixture) -> TaskStateReport {
385
+ validate_task_state(
386
+ repo.path(),
387
+ TaskStateOptions {
388
+ mode: TaskStateMode::CommitGate,
389
+ ..TaskStateOptions::default()
390
+ },
701
391
  )
702
- .unwrap();
392
+ .unwrap()
703
393
  }
704
394
 
705
- fn idle_task_state() -> Value {
706
- json!({
707
- "schema": "naome.task-state.v1",
708
- "version": 1,
709
- "status": "idle",
710
- "activeTask": null,
711
- "blocker": null,
712
- "updatedAt": null
713
- })
395
+ fn progress_report(repo: &TaskFixture) -> TaskStateReport {
396
+ validate_task_state(
397
+ repo.path(),
398
+ TaskStateOptions {
399
+ mode: TaskStateMode::Progress,
400
+ ..TaskStateOptions::default()
401
+ },
402
+ )
403
+ .unwrap()
714
404
  }
715
405
 
716
- fn complete_task_state(overrides: Value) -> Value {
406
+ fn implementing_readme_task() -> serde_json::Value {
717
407
  json!({
718
408
  "schema": "naome.task-state.v1",
719
409
  "version": 1,
720
- "status": "complete",
721
- "activeTask": active_task(overrides),
410
+ "status": "implementing",
411
+ "activeTask": active_task(json!({
412
+ "allowedPaths": ["README.md"],
413
+ "proofResults": []
414
+ })),
722
415
  "blocker": null,
723
416
  "updatedAt": "2026-05-04T12:00:00.000Z"
724
417
  })
725
418
  }
726
-
727
- fn active_task(overrides: Value) -> Value {
728
- let mut task = json!({
729
- "id": "readme-test",
730
- "request": "Update the README.",
731
- "userPrompt": {
732
- "receivedAt": "2026-05-04T12:00:00.000Z",
733
- "text": "Update the README."
734
- },
735
- "admission": successful_admission(json!({})),
736
- "allowedPaths": ["README.md"],
737
- "declaredChangeTypes": ["docs"],
738
- "requiredCheckIds": ["diff-check"],
739
- "proofResults": [],
740
- "humanReview": {
741
- "required": false,
742
- "approved": false,
743
- "reason": null
744
- }
745
- });
746
- merge_object(&mut task, overrides);
747
- task
748
- }
749
-
750
- fn successful_admission(overrides: Value) -> Value {
751
- let mut admission = json!({
752
- "command": "node .naome/bin/check-task-state.js --admission",
753
- "cwd": ".",
754
- "exitCode": 0,
755
- "checkedAt": "2026-05-04T12:00:00.000Z",
756
- "gitHead": "pending-test-head",
757
- "changedPaths": []
758
- });
759
- merge_object(&mut admission, overrides);
760
- admission
761
- }
762
-
763
- fn successful_proof(overrides: Value) -> Value {
764
- let mut proof = json!({
765
- "checkId": "diff-check",
766
- "command": "git diff --check",
767
- "cwd": ".",
768
- "exitCode": 0,
769
- "checkedAt": "2026-05-04T12:00:00.000Z",
770
- "evidence": ["README.md"]
771
- });
772
- merge_object(&mut proof, overrides);
773
- proof
774
- }
775
-
776
- fn merge_object(target: &mut Value, overrides: Value) {
777
- let Some(target_object) = target.as_object_mut() else {
778
- return;
779
- };
780
- let Some(overrides_object) = overrides.as_object() else {
781
- return;
782
- };
783
-
784
- for (key, value) in overrides_object {
785
- target_object.insert(key.clone(), value.clone());
786
- }
787
- }
@@ -0,0 +1,110 @@
1
+ mod task_state_compact_support;
2
+
3
+ use naome_core::{evaluate_route, EvaluationOptions, RouteOptions, TaskStateMode};
4
+ use serde_json::{json, Value};
5
+ use task_state_compact_support::{
6
+ compact_state, large_compact_state, large_expanded_state, legacy_state, MiniRepo,
7
+ };
8
+
9
+ #[test]
10
+ fn legacy_v1_proof_results_remain_valid() {
11
+ let repo = MiniRepo::new();
12
+ let head = repo.seed();
13
+ repo.change_readme();
14
+ repo.write_task_state(legacy_state(&head));
15
+
16
+ let report = repo.validate(TaskStateMode::State);
17
+
18
+ assert_eq!(report.errors, Vec::<String>::new());
19
+ }
20
+
21
+ #[test]
22
+ fn compact_path_sets_and_batches_cover_completion_commit_and_route() {
23
+ let repo = MiniRepo::new();
24
+ let head = repo.seed();
25
+ repo.change_readme();
26
+ repo.write_task_state(compact_state(&head));
27
+
28
+ let completion = repo.validate(TaskStateMode::State);
29
+ assert_eq!(completion.errors, Vec::<String>::new());
30
+
31
+ let route = evaluate_route(
32
+ repo.path(),
33
+ "new task",
34
+ RouteOptions {
35
+ execute: false,
36
+ evaluation: EvaluationOptions::offline(),
37
+ },
38
+ )
39
+ .unwrap();
40
+ assert_eq!(
41
+ route.policy_action,
42
+ "auto_commit_completed_task_then_create_new_task"
43
+ );
44
+
45
+ repo.git(["add", "README.md", ".naome/task-state.json"]);
46
+ let commit_gate = repo.validate(TaskStateMode::CommitGate);
47
+ assert_eq!(commit_gate.errors, Vec::<String>::new());
48
+ }
49
+
50
+ #[test]
51
+ fn compact_missing_required_proof_is_reported() {
52
+ let repo = MiniRepo::new();
53
+ let head = repo.seed();
54
+ repo.change_readme();
55
+ let mut state = compact_state(&head);
56
+ state["activeTask"]["proofBatches"][0]["proofs"] = json!([
57
+ {
58
+ "checkId": "alpha",
59
+ "exitCode": 0
60
+ }
61
+ ]);
62
+ repo.write_task_state(state);
63
+
64
+ let report = repo.validate(TaskStateMode::State);
65
+
66
+ assert!(
67
+ report
68
+ .errors
69
+ .iter()
70
+ .any(|error| error.contains("missing proof result: beta")),
71
+ "{:?}",
72
+ report.errors
73
+ );
74
+ }
75
+
76
+ #[test]
77
+ fn compact_missing_evidence_is_reported() {
78
+ let repo = MiniRepo::new();
79
+ let head = repo.seed();
80
+ repo.change_readme();
81
+ let mut state = compact_state(&head);
82
+ state["activeTask"]["proofBatches"][0]["evidencePathSet"] = Value::Null;
83
+ state["activeTask"]["proofBatches"][0]["proofs"][0]["evidencePathSet"] = Value::Null;
84
+ repo.write_task_state(state);
85
+
86
+ let report = repo.validate(TaskStateMode::State);
87
+
88
+ assert!(
89
+ report
90
+ .errors
91
+ .iter()
92
+ .any(|error| error.contains("evidence must be an evidence array or path-set reference")),
93
+ "{:?}",
94
+ report.errors
95
+ );
96
+ }
97
+
98
+ #[test]
99
+ fn compact_writer_payload_is_smaller_than_expanded_equivalent() {
100
+ let compact = large_compact_state("abcdef");
101
+ let expanded = large_expanded_state("abcdef");
102
+
103
+ let compact_len = serde_json::to_string(&compact).unwrap().len();
104
+ let expanded_len = serde_json::to_string(&expanded).unwrap().len();
105
+
106
+ assert!(
107
+ compact_len * 100 / expanded_len < 75,
108
+ "compact={compact_len} expanded={expanded_len}"
109
+ );
110
+ }
@@ -0,0 +1,5 @@
1
+ pub mod repo;
2
+ pub mod states;
3
+
4
+ pub use repo::MiniRepo;
5
+ pub use states::{compact_state, large_compact_state, large_expanded_state, legacy_state};