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