@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,826 +1,131 @@
1
- use std::fs;
2
- use std::path::{Path, PathBuf};
3
- use std::process::Command;
4
- use std::time::{SystemTime, UNIX_EPOCH};
1
+ mod intent_support;
5
2
 
6
- use naome_core::{evaluate_intent, EvaluationOptions};
7
- use serde_json::json;
3
+ use naome_core::IntentDecision;
8
4
 
9
- #[test]
10
- fn clean_repo_with_new_goal_creates_task() {
11
- let repo = TestRepo::new("intent-clean-new-task");
12
- repo.init_git();
13
- repo.write_file("README.md", "# Baseline\n");
14
- repo.write_base_naome_state(json!({ "status": "idle", "activeTask": null }));
15
- repo.git(&["add", "."]);
16
- repo.git(&["commit", "-m", "baseline"]);
17
-
18
- let intent = evaluate_intent(
19
- repo.path(),
20
- "Bitte implementiere eine neue kleine Funktion.",
21
- EvaluationOptions::offline(),
22
- )
23
- .unwrap();
24
-
25
- assert_eq!(intent.repo_state, "ready_for_task");
26
- assert_eq!(intent.prompt_intent, "new_task");
27
- assert_eq!(intent.policy_action, "create_new_task");
28
- assert!(intent.allowed);
29
- }
5
+ use intent_support::{completed_state, env, idle, Repo};
30
6
 
31
7
  #[test]
32
- fn completed_setup_diff_and_new_goal_auto_baselines_before_new_task() {
33
- let repo = TestRepo::new("intent-setup-new-task");
34
- repo.init_git();
35
- repo.write_file("README.md", "# Baseline\n");
36
- repo.write_base_naome_state(json!({ "status": "idle", "activeTask": null }));
37
- repo.write_naome_json(
38
- "manifest.json",
39
- json!({
40
- "machineOwned": ["AGENTS.md"],
41
- "projectOwned": [
42
- ".naome/init-state.json",
43
- ".naome/task-state.json",
44
- ".naome/upgrade-state.json",
45
- ".naome/verification.json",
46
- "docs/naome/**"
47
- ],
48
- "integrity": {}
49
- }),
8
+ fn repo_state_and_envelope_resolve_task_policy() {
9
+ let clean = Repo::clean("clean", idle());
10
+ assert_intent(
11
+ &clean.intent("Please implement a small new feature."),
12
+ "ready_for_task",
13
+ "new_task",
14
+ "create_new_task",
15
+ true,
50
16
  );
51
- repo.git(&["add", "."]);
52
- repo.git(&["commit", "-m", "baseline"]);
53
- repo.write_naome_json(
54
- "init-state.json",
55
- json!({
56
- "initialized": true,
57
- "intakeStatus": "complete",
58
- "initializedBy": "codex"
59
- }),
60
- );
61
-
62
- let intent = evaluate_intent(
63
- repo.path(),
64
- "Please add a README summary as a new task.",
65
- EvaluationOptions::offline(),
66
- )
67
- .unwrap();
68
17
 
69
- assert_eq!(intent.repo_state, "install_or_upgrade_unbaselined");
70
- assert_eq!(intent.prompt_intent, "new_task");
71
- assert_eq!(
72
- intent.policy_action,
73
- "auto_commit_upgrade_baseline_then_create_new_task"
18
+ let mut active_state = completed_state("HEAD", true);
19
+ active_state["status"] = serde_json::json!("implementing");
20
+ let active = Repo::clean("active", active_state);
21
+ let revision = active.intent(&env("follow up", "none", "task_revision", "none"));
22
+ assert_intent(
23
+ &revision,
24
+ "active_task_in_progress",
25
+ "task_revision",
26
+ "continue_current_task",
27
+ true,
74
28
  );
75
- assert!(intent.allowed);
76
- }
77
-
78
- #[test]
79
- fn active_task_with_distinct_new_goal_blocks_instead_of_continuing() {
80
- let repo = TestRepo::new("intent-active-new-task");
81
- repo.init_git();
82
- repo.write_file("README.md", "# Baseline\n");
83
- repo.write_base_naome_state(active_task_state("implementing"));
84
- repo.git(&["add", "."]);
85
- repo.git(&["commit", "-m", "baseline"]);
86
-
87
- let intent = evaluate_intent(
88
- repo.path(),
89
- "Jetzt implementiere bitte noch ein neues Release-System.",
90
- EvaluationOptions::offline(),
91
- )
92
- .unwrap();
93
-
94
- assert_eq!(intent.repo_state, "active_task_in_progress");
95
- assert_eq!(intent.prompt_intent, "new_task");
96
- assert_eq!(intent.policy_action, "block_ambiguous_intent");
97
- assert!(!intent.allowed);
98
- }
99
-
100
- #[test]
101
- fn active_task_with_correction_continues_current_task() {
102
- let repo = TestRepo::new("intent-active-correction");
103
- repo.init_git();
104
- repo.write_file("README.md", "# Baseline\n");
105
- repo.write_base_naome_state(active_task_state("implementing"));
106
- repo.git(&["add", "."]);
107
- repo.git(&["commit", "-m", "baseline"]);
108
-
109
- let intent = evaluate_intent(
110
- repo.path(),
111
- "Mach bitte diese Kleinigkeit im aktuellen Task auch noch.",
112
- EvaluationOptions::offline(),
113
- )
114
- .unwrap();
115
-
116
- assert_eq!(intent.repo_state, "active_task_in_progress");
117
- assert_eq!(intent.prompt_intent, "task_revision");
118
- assert_eq!(intent.policy_action, "continue_current_task");
119
- assert!(intent.allowed);
120
- }
121
-
122
- #[test]
123
- fn completed_valid_task_and_new_goal_can_auto_baseline_then_create() {
124
- let repo = TestRepo::new("intent-complete-new-task");
125
- repo.init_git();
126
- repo.write_file("README.md", "# Baseline\n");
127
- repo.write_base_naome_state(json!({ "status": "idle", "activeTask": null }));
128
- repo.git(&["add", "."]);
129
- repo.git(&["commit", "-m", "baseline"]);
130
- let admission_head = repo.git_stdout(&["rev-parse", "HEAD"]);
131
- repo.write_base_naome_state(completed_task_state(&admission_head, true));
132
- repo.git(&["add", ".naome/task-state.json"]);
133
- repo.git(&["commit", "-m", "task state"]);
134
- repo.write_file("README.md", "# Changed\n");
135
-
136
- let intent = evaluate_intent(
137
- repo.path(),
138
- "Danach implementiere bitte einen neuen Task.",
139
- EvaluationOptions::offline(),
140
- )
141
- .unwrap();
142
-
143
- assert_eq!(intent.repo_state, "completed_task_unbaselined");
144
- assert_eq!(intent.prompt_intent, "new_task");
145
- assert_eq!(
146
- intent.policy_action,
147
- "auto_commit_completed_task_then_create_new_task"
148
- );
149
- assert!(intent.allowed);
150
- }
151
-
152
- #[test]
153
- fn completed_valid_task_with_unrelated_user_edit_creates_isolated_worktree() {
154
- let repo = TestRepo::new("intent-complete-new-task-with-user-edit");
155
- repo.init_git();
156
- repo.write_file("README.md", "# Baseline\n");
157
- repo.write_file("USER.md", "user baseline\n");
158
- repo.write_base_naome_state(json!({ "status": "idle", "activeTask": null }));
159
- repo.git(&["add", "."]);
160
- repo.git(&["commit", "-m", "baseline"]);
161
- let admission_head = repo.git_stdout(&["rev-parse", "HEAD"]);
162
- repo.write_base_naome_state(completed_task_state(&admission_head, true));
163
- repo.git(&["add", ".naome/task-state.json"]);
164
- repo.git(&["commit", "-m", "task state"]);
165
- repo.write_file("README.md", "# Changed\n");
166
- repo.write_file("USER.md", "user local edit\n");
167
-
168
- let intent = evaluate_intent(
169
- repo.path(),
170
- "Add another line to README as a new task.",
171
- EvaluationOptions::offline(),
172
- )
173
- .unwrap();
174
-
175
- assert_eq!(intent.repo_state, "completed_task_unbaselined");
176
- assert_eq!(intent.prompt_intent, "new_task");
177
- assert_eq!(
178
- intent.policy_action,
179
- "auto_commit_completed_task_then_create_isolated_task_worktree"
29
+ let distinct = active.intent(&env("separate work", "none", "new_task", "none"));
30
+ assert_intent(
31
+ &distinct,
32
+ "active_task_in_progress",
33
+ "new_task",
34
+ "block_ambiguous_intent",
35
+ false,
180
36
  );
181
- assert!(intent.allowed);
182
- assert!(intent.human_options.is_empty());
183
- assert!(intent.user_message.contains("isolated worktree"));
184
- }
185
37
 
186
- #[test]
187
- fn dirty_repo_with_harness_refresh_and_user_edit_baselines_refresh_before_worktree() {
188
- let repo = TestRepo::new("intent-dirty-harness-refresh-with-user-edit");
189
- repo.init_git();
190
- repo.write_file("README.md", "# Baseline\n");
191
- repo.write_file("USER.md", "user baseline\n");
192
- repo.write_file("AGENTS.md", "# Agent Instructions\n\nOld harness.\n");
193
- repo.write_base_naome_state(json!({ "status": "idle", "activeTask": null }));
194
- repo.write_naome_json(
195
- "manifest.json",
196
- json!({
197
- "name": "naome",
198
- "harnessVersion": "1.1.0",
199
- "profile": "standard",
200
- "machineOwned": ["AGENTS.md"],
201
- "projectOwned": [".naome/manifest.json", ".naome/task-state.json"],
202
- "integrity": {}
203
- }),
204
- );
205
- repo.git(&["add", "."]);
206
- repo.git(&["commit", "-m", "baseline"]);
207
- repo.write_file("AGENTS.md", "# Agent Instructions\n\nUpdated by sync.\n");
208
- repo.write_naome_json(
209
- "manifest.json",
210
- json!({
211
- "name": "naome",
212
- "harnessVersion": "1.1.0",
213
- "profile": "standard",
214
- "machineOwned": ["AGENTS.md"],
215
- "projectOwned": [".naome/manifest.json", ".naome/task-state.json"],
216
- "integrity": {}
217
- }),
38
+ let completed = Repo::completed("completed", true);
39
+ let next = completed.intent("After that, please implement a new task.");
40
+ assert_intent(
41
+ &next,
42
+ "completed_task_unbaselined",
43
+ "new_task",
44
+ "auto_commit_completed_task_then_create_new_task",
45
+ true,
218
46
  );
219
- repo.write_file("USER.md", "user local edit\n");
220
-
221
- let intent = evaluate_intent(
222
- repo.path(),
223
- "Add another line to README as a new task.",
224
- EvaluationOptions::offline(),
225
- )
226
- .unwrap();
227
-
228
- assert_eq!(intent.repo_state, "dirty_unowned_diff");
229
47
  assert_eq!(
230
- intent.policy_action,
231
- "auto_commit_harness_refresh_then_create_isolated_task_worktree"
48
+ completed
49
+ .intent(&env("review", "review_request", "new_task", "none"))
50
+ .policy_action,
51
+ "review_task_diff"
232
52
  );
233
- assert!(intent.allowed);
234
- assert!(intent.human_options.is_empty());
235
- }
236
-
237
- #[test]
238
- fn completed_task_state_with_only_harness_refresh_baselines_before_new_task() {
239
- let repo = TestRepo::new("intent-complete-pure-harness-refresh");
240
- repo.init_git();
241
- repo.write_file("README.md", "# Baseline\n");
242
- repo.write_file("AGENTS.md", "# Agent Instructions\n\nOld harness.\n");
243
- repo.write_base_naome_state(json!({ "status": "idle", "activeTask": null }));
244
- repo.write_naome_json(
245
- "manifest.json",
246
- json!({
247
- "name": "naome",
248
- "harnessVersion": "1.1.0",
249
- "profile": "old",
250
- "machineOwned": ["AGENTS.md"],
251
- "projectOwned": [".naome/manifest.json", ".naome/task-state.json"],
252
- "integrity": {}
253
- }),
53
+ assert_eq!(
54
+ completed
55
+ .intent(&env("cancel", "cancel_request", "new_task", "none"))
56
+ .policy_action,
57
+ "cancel_task_changes"
254
58
  );
255
- repo.git(&["add", "."]);
256
- repo.git(&["commit", "-m", "baseline"]);
257
- let admission_head = repo.git_stdout(&["rev-parse", "HEAD"]);
258
- repo.write_base_naome_state(completed_task_state(&admission_head, true));
259
- repo.git(&["add", ".naome/task-state.json"]);
260
- repo.git(&["commit", "-m", "task state"]);
261
- repo.write_file("AGENTS.md", "# Agent Instructions\n\nUpdated by sync.\n");
262
- repo.write_naome_json(
263
- "manifest.json",
264
- json!({
265
- "name": "naome",
266
- "harnessVersion": "1.1.0",
267
- "profile": "standard",
268
- "machineOwned": ["AGENTS.md"],
269
- "projectOwned": [".naome/manifest.json", ".naome/task-state.json"],
270
- "integrity": {}
271
- }),
59
+ assert_eq!(
60
+ completed
61
+ .intent(&env("no commit", "no_commit_request", "new_task", "none"))
62
+ .policy_action,
63
+ "block_auto_baseline_due_to_no_commit"
272
64
  );
273
-
274
- let intent = evaluate_intent(
275
- repo.path(),
276
- "Add another line to README as a new task.",
277
- EvaluationOptions::offline(),
278
- )
279
- .unwrap();
280
-
281
- assert_eq!(intent.repo_state, "harness_repair_unbaselined");
282
- assert_eq!(intent.prompt_intent, "new_task");
283
65
  assert_eq!(
284
- intent.policy_action,
285
- "auto_commit_harness_refresh_then_create_new_task"
66
+ completed
67
+ .intent(&env("revise", "none", "task_revision", "none"))
68
+ .policy_action,
69
+ "reopen_completed_task_revision"
286
70
  );
287
- assert!(intent.allowed);
288
- assert!(intent.human_options.is_empty());
289
- assert!(!intent.user_message.contains("isolated worktree"));
290
71
  }
291
72
 
292
73
  #[test]
293
- fn completed_task_state_with_manifest_only_diff_is_not_harness_refresh() {
294
- let repo = TestRepo::new("intent-complete-manifest-only-diff");
295
- repo.init_git();
296
- repo.write_file("README.md", "# Baseline\n");
297
- repo.write_base_naome_state(json!({ "status": "idle", "activeTask": null }));
298
- repo.write_naome_json(
299
- "manifest.json",
300
- json!({
301
- "name": "naome",
302
- "harnessVersion": "1.1.0",
303
- "profile": "standard",
304
- "machineOwned": ["AGENTS.md"],
305
- "projectOwned": [".naome/manifest.json", ".naome/task-state.json"],
306
- "integrity": {}
307
- }),
308
- );
309
- repo.git(&["add", "."]);
310
- repo.git(&["commit", "-m", "baseline"]);
311
- let admission_head = repo.git_stdout(&["rev-parse", "HEAD"]);
312
- repo.write_base_naome_state(completed_task_state(&admission_head, true));
313
- repo.git(&["add", ".naome/task-state.json"]);
314
- repo.git(&["commit", "-m", "task state"]);
315
- repo.write_naome_json(
316
- "manifest.json",
317
- json!({
318
- "name": "naome",
319
- "harnessVersion": "9.9.9",
320
- "profile": "standard",
321
- "machineOwned": ["AGENTS.md"],
322
- "projectOwned": [".naome/manifest.json", ".naome/task-state.json"],
323
- "integrity": {}
324
- }),
74
+ fn workflow_and_risk_intents_are_structured_not_language_keywords() {
75
+ let status =
76
+ Repo::clean("status", idle()).intent(&env("anything", "status_question", "none", "none"));
77
+ assert_intent(
78
+ &status,
79
+ "ready_for_task",
80
+ "status_question",
81
+ "answer_status_only",
82
+ true,
325
83
  );
326
84
 
327
- let intent = evaluate_intent(
328
- repo.path(),
329
- "Add another line to README as a new task.",
330
- EvaluationOptions::offline(),
331
- )
332
- .unwrap();
333
-
334
- assert_eq!(intent.repo_state, "dirty_unowned_diff");
335
- assert_eq!(intent.policy_action, "create_isolated_task_worktree");
336
- }
337
-
338
- #[test]
339
- fn dirty_repo_repair_request_baselines_only_harness_refresh() {
340
- let repo = TestRepo::new("intent-dirty-harness-refresh-repair-request");
341
- repo.init_git();
342
- repo.write_file("README.md", "# Baseline\n");
343
- repo.write_file("USER.md", "user baseline\n");
344
- repo.write_file("AGENTS.md", "# Agent Instructions\n\nOld harness.\n");
345
- repo.write_base_naome_state(json!({ "status": "idle", "activeTask": null }));
346
- repo.write_naome_json(
347
- "manifest.json",
348
- json!({
349
- "name": "naome",
350
- "harnessVersion": "1.1.0",
351
- "profile": "standard",
352
- "machineOwned": ["AGENTS.md"],
353
- "projectOwned": [".naome/manifest.json", ".naome/task-state.json"],
354
- "integrity": {}
355
- }),
356
- );
357
- repo.git(&["add", "."]);
358
- repo.git(&["commit", "-m", "baseline"]);
359
- repo.write_file("AGENTS.md", "# Agent Instructions\n\nUpdated by sync.\n");
360
- repo.write_naome_json(
361
- "manifest.json",
362
- json!({
363
- "name": "naome",
364
- "harnessVersion": "1.1.0",
365
- "profile": "standard",
366
- "machineOwned": ["AGENTS.md"],
367
- "projectOwned": [".naome/manifest.json", ".naome/task-state.json"],
368
- "integrity": {}
369
- }),
85
+ let dirty = Repo::clean("dirty", idle());
86
+ dirty.write("README.md", "# Manual edit\n");
87
+ let commit = dirty.intent(&env("okay commit it", "commit_request", "none", "none"));
88
+ assert_intent(
89
+ &commit,
90
+ "dirty_unowned_diff",
91
+ "commit_request",
92
+ "commit_user_diff_with_quality_gate",
93
+ true,
370
94
  );
371
- repo.write_file("USER.md", "user local edit\n");
372
-
373
- let intent = evaluate_intent(
374
- repo.path(),
375
- "please repair all",
376
- EvaluationOptions::offline(),
377
- )
378
- .unwrap();
379
-
380
- assert_eq!(intent.repo_state, "dirty_unowned_diff");
381
- assert_eq!(intent.prompt_intent, "repair_request");
382
- assert_eq!(intent.policy_action, "auto_commit_harness_refresh_baseline");
383
- assert!(intent.allowed);
384
- assert!(intent.human_options.is_empty());
385
- }
386
-
387
- #[test]
388
- fn dirty_unowned_commit_request_does_not_offer_clear_or_commit_fallback() {
389
- let repo = TestRepo::new("intent-dirty-commit-request");
390
- repo.init_git();
391
- repo.write_file("README.md", "# Baseline\n");
392
- repo.write_base_naome_state(json!({ "status": "idle", "activeTask": null }));
393
- repo.git(&["add", "."]);
394
- repo.git(&["commit", "-m", "baseline"]);
395
- repo.write_file("README.md", "# Manual edit\n");
396
-
397
- let intent =
398
- evaluate_intent(repo.path(), "okay commit it", EvaluationOptions::offline()).unwrap();
399
-
400
- assert_eq!(intent.repo_state, "dirty_unowned_diff");
401
- assert_eq!(intent.prompt_intent, "commit_request");
402
- assert_eq!(intent.policy_action, "commit_user_diff_with_quality_gate");
403
- assert!(intent.allowed);
404
- assert!(!intent
405
- .human_options
406
- .contains(&"clear_or_commit_unowned_diff".to_string()));
407
- assert!(intent.user_message.contains("quality gate"));
408
- }
409
-
410
- #[test]
411
- fn unsafe_terms_win_over_commit_and_new_task_requests() {
412
- let repo = TestRepo::new("intent-unsafe-priority");
413
- repo.init_git();
414
- repo.write_file("README.md", "# Baseline\n");
415
- repo.write_base_naome_state(json!({ "status": "idle", "activeTask": null }));
416
- repo.git(&["add", "."]);
417
- repo.git(&["commit", "-m", "baseline"]);
418
95
 
419
- let intent = evaluate_intent(
420
- repo.path(),
421
- "Create a new task, commit it, then push with --no-verify and include this API key.",
422
- EvaluationOptions::offline(),
423
- )
424
- .unwrap();
425
-
426
- assert_eq!(intent.prompt_intent, "unsafe");
427
- assert_eq!(intent.policy_action, "block_unsafe_intent");
428
- assert!(!intent.allowed);
429
- assert!(!intent.human_options.is_empty());
430
- assert!(intent
431
- .internal_notes
432
- .contains(&"winning_rule:unsafe_intent_precedence".to_string()));
433
- }
434
-
435
- #[test]
436
- fn do_not_commit_blocks_completed_task_auto_baseline() {
437
- let repo = TestRepo::new("intent-no-commit-priority");
438
- repo.init_git();
439
- repo.write_file("README.md", "# Baseline\n");
440
- repo.write_base_naome_state(json!({ "status": "idle", "activeTask": null }));
441
- repo.git(&["add", "."]);
442
- repo.git(&["commit", "-m", "baseline"]);
443
- let admission_head = repo.git_stdout(&["rev-parse", "HEAD"]);
444
- repo.write_base_naome_state(completed_task_state(&admission_head, true));
445
- repo.git(&["add", ".naome/task-state.json"]);
446
- repo.git(&["commit", "-m", "task state"]);
447
- repo.write_file("README.md", "# Changed\n");
448
-
449
- let intent = evaluate_intent(
450
- repo.path(),
451
- "Do not commit. Please start a new task after this.",
452
- EvaluationOptions::offline(),
453
- )
454
- .unwrap();
455
-
456
- assert_eq!(intent.prompt_intent, "no_commit_request");
457
- assert_eq!(intent.policy_action, "block_auto_baseline_due_to_no_commit");
458
- assert!(!intent.allowed);
459
- assert!(intent
460
- .human_options
461
- .contains(&"review_task_diff".to_string()));
462
- }
463
-
464
- #[test]
465
- fn explicit_review_request_overrides_auto_baseline() {
466
- let repo = TestRepo::new("intent-review-priority");
467
- repo.init_git();
468
- repo.write_file("README.md", "# Baseline\n");
469
- repo.write_base_naome_state(json!({ "status": "idle", "activeTask": null }));
470
- repo.git(&["add", "."]);
471
- repo.git(&["commit", "-m", "baseline"]);
472
- let admission_head = repo.git_stdout(&["rev-parse", "HEAD"]);
473
- repo.write_base_naome_state(completed_task_state(&admission_head, true));
474
- repo.git(&["add", ".naome/task-state.json"]);
475
- repo.git(&["commit", "-m", "task state"]);
476
- repo.write_file("README.md", "# Changed\n");
477
-
478
- let intent = evaluate_intent(
479
- repo.path(),
480
- "Review the current task diff, then also create a new task.",
481
- EvaluationOptions::offline(),
482
- )
483
- .unwrap();
484
-
485
- assert_eq!(intent.prompt_intent, "review_request");
486
- assert_eq!(intent.policy_action, "review_task_diff");
487
- assert!(!intent.allowed);
488
- assert!(intent
489
- .human_options
490
- .contains(&"commit_task_baseline".to_string()));
491
- }
492
-
493
- #[test]
494
- fn explicit_cancel_request_overrides_auto_baseline() {
495
- let repo = TestRepo::new("intent-cancel-priority");
496
- repo.init_git();
497
- repo.write_file("README.md", "# Baseline\n");
498
- repo.write_base_naome_state(json!({ "status": "idle", "activeTask": null }));
499
- repo.git(&["add", "."]);
500
- repo.git(&["commit", "-m", "baseline"]);
501
- let admission_head = repo.git_stdout(&["rev-parse", "HEAD"]);
502
- repo.write_base_naome_state(completed_task_state(&admission_head, true));
503
- repo.git(&["add", ".naome/task-state.json"]);
504
- repo.git(&["commit", "-m", "task state"]);
505
- repo.write_file("README.md", "# Changed\n");
506
-
507
- let intent = evaluate_intent(
508
- repo.path(),
509
- "Cancel the current task changes, but also start a new task.",
510
- EvaluationOptions::offline(),
511
- )
512
- .unwrap();
513
-
514
- assert_eq!(intent.prompt_intent, "cancel_request");
515
- assert_eq!(intent.policy_action, "cancel_task_changes");
516
- assert!(!intent.allowed);
517
- assert!(intent
518
- .human_options
519
- .contains(&"review_task_diff".to_string()));
520
- }
521
-
522
- #[test]
523
- fn explicit_new_task_wins_over_vague_continuation_wording() {
524
- let repo = TestRepo::new("intent-new-over-vague-continuation");
525
- repo.init_git();
526
- repo.write_file("README.md", "# Baseline\n");
527
- repo.write_base_naome_state(json!({ "status": "idle", "activeTask": null }));
528
- repo.git(&["add", "."]);
529
- repo.git(&["commit", "-m", "baseline"]);
530
-
531
- let intent = evaluate_intent(
532
- repo.path(),
533
- "Als neuer Task: mach bitte auch noch eine kurze README Ergänzung.",
534
- EvaluationOptions::offline(),
535
- )
536
- .unwrap();
537
-
538
- assert_eq!(intent.prompt_intent, "new_task");
539
- assert_eq!(intent.policy_action, "create_new_task");
540
- assert!(intent.allowed);
541
- }
542
-
543
- #[test]
544
- fn status_requests_do_not_offer_mutating_human_options() {
545
- let repo = TestRepo::new("intent-status-readonly");
546
- repo.init_git();
547
- repo.write_file("README.md", "# Baseline\n");
548
- repo.write_base_naome_state(json!({ "status": "idle", "activeTask": null }));
549
- repo.git(&["add", "."]);
550
- repo.git(&["commit", "-m", "baseline"]);
551
-
552
- let intent = evaluate_intent(
553
- repo.path(),
554
- "What is the current NAOME status and why?",
555
- EvaluationOptions::offline(),
556
- )
557
- .unwrap();
96
+ let deprecated = dirty.intent("clear_or_commit_unowned_diff");
97
+ assert_intent(
98
+ &deprecated,
99
+ "dirty_unowned_diff",
100
+ "ambiguous",
101
+ "block_unowned_diff",
102
+ false,
103
+ );
558
104
 
559
- assert_eq!(intent.prompt_intent, "status_question");
560
- assert_eq!(intent.policy_action, "answer_status_only");
561
- assert!(intent.allowed);
562
- assert!(intent.human_options.is_empty());
563
- assert!(!intent.user_message.contains("commit_task_baseline"));
105
+ let risky = Repo::clean("risky", idle()).intent(&env(
106
+ "include this API key",
107
+ "commit_request",
108
+ "new_task",
109
+ "credential_context",
110
+ ));
111
+ assert_eq!(risky.prompt_intent, "unsafe");
112
+ assert_eq!(risky.policy_action, "block_unsafe_intent");
113
+ assert!(!risky.allowed);
564
114
  }
565
115
 
566
116
  #[test]
567
- fn completed_invalid_task_and_new_goal_blocks_auto_baseline() {
568
- let repo = TestRepo::new("intent-invalid-complete-new-task");
569
- repo.init_git();
570
- repo.write_file("README.md", "# Baseline\n");
571
- repo.write_base_naome_state(json!({ "status": "idle", "activeTask": null }));
572
- repo.git(&["add", "."]);
573
- repo.git(&["commit", "-m", "baseline"]);
574
- let admission_head = repo.git_stdout(&["rev-parse", "HEAD"]);
575
- repo.write_base_naome_state(completed_task_state(&admission_head, false));
576
- repo.git(&["add", ".naome/task-state.json"]);
577
- repo.git(&["commit", "-m", "task state"]);
578
- repo.write_file("README.md", "# Changed\n");
579
-
580
- let intent = evaluate_intent(
581
- repo.path(),
582
- "Danach implementiere bitte einen neuen Task.",
583
- EvaluationOptions::offline(),
584
- )
585
- .unwrap();
586
-
587
- assert_eq!(intent.repo_state, "completed_task_unbaselined");
117
+ fn invalid_completed_task_proof_blocks_new_task_baseline() {
118
+ let repo = Repo::completed("invalid", false);
119
+ let intent = repo.intent("After that, please implement a new task.");
588
120
  assert_eq!(intent.policy_action, "block_unsafe_intent");
589
- assert!(!intent.allowed);
590
121
  assert!(intent
591
122
  .risk_codes
592
123
  .contains(&"completed_task_proof_invalid".to_string()));
593
124
  }
594
125
 
595
- #[test]
596
- fn completed_task_correction_reopens_same_task_revision() {
597
- let repo = TestRepo::new("intent-complete-correction");
598
- repo.init_git();
599
- repo.write_file("README.md", "# Baseline\n");
600
- repo.write_base_naome_state(json!({ "status": "idle", "activeTask": null }));
601
- repo.git(&["add", "."]);
602
- repo.git(&["commit", "-m", "baseline"]);
603
- let admission_head = repo.git_stdout(&["rev-parse", "HEAD"]);
604
- repo.write_base_naome_state(completed_task_state(&admission_head, true));
605
- repo.git(&["add", ".naome/task-state.json"]);
606
- repo.git(&["commit", "-m", "task state"]);
607
- repo.write_file("README.md", "# Changed\n");
608
-
609
- let intent = evaluate_intent(
610
- repo.path(),
611
- "Bitte im gleichen Task noch eine Kleinigkeit korrigieren.",
612
- EvaluationOptions::offline(),
613
- )
614
- .unwrap();
615
-
616
- assert_eq!(intent.prompt_intent, "task_revision");
617
- assert_eq!(intent.policy_action, "reopen_completed_task_revision");
618
- assert!(intent.allowed);
619
- }
620
-
621
- #[test]
622
- fn risky_prompt_without_clear_task_transition_blocks_unsafe() {
623
- let repo = TestRepo::new("intent-risky-unclear");
624
- repo.init_git();
625
- repo.write_file("README.md", "# Baseline\n");
626
- repo.write_base_naome_state(json!({ "status": "idle", "activeTask": null }));
627
- repo.git(&["add", "."]);
628
- repo.git(&["commit", "-m", "baseline"]);
629
-
630
- let intent = evaluate_intent(
631
- repo.path(),
632
- "Hier ist ein API key und bitte --no-verify.",
633
- EvaluationOptions::offline(),
634
- )
635
- .unwrap();
636
-
637
- assert_eq!(intent.prompt_intent, "unsafe");
638
- assert_eq!(intent.policy_action, "block_unsafe_intent");
639
- assert!(!intent.allowed);
640
- }
641
-
642
- fn active_task_state(status: &str) -> serde_json::Value {
643
- json!({
644
- "schema": "naome.task-state.v1",
645
- "version": 1,
646
- "status": status,
647
- "activeTask": {
648
- "id": "readme-task",
649
- "request": "Change README.",
650
- "userPrompt": {
651
- "receivedAt": "2026-05-06T00:00:00.000Z",
652
- "text": "Change README."
653
- },
654
- "admission": {
655
- "command": "node .naome/bin/check-task-state.js --admission",
656
- "cwd": ".",
657
- "exitCode": 0,
658
- "checkedAt": "2026-05-06T00:00:00.000Z",
659
- "gitHead": "",
660
- "changedPaths": []
661
- },
662
- "allowedPaths": ["README.md"],
663
- "declaredChangeTypes": ["product-docs"],
664
- "requiredCheckIds": ["diff-check"],
665
- "proofResults": [],
666
- "revisions": [],
667
- "humanReview": {
668
- "required": false,
669
- "approved": false,
670
- "reason": null
671
- }
672
- },
673
- "blocker": null,
674
- "updatedAt": "2026-05-06T00:00:00.000Z"
675
- })
676
- }
677
-
678
- fn completed_task_state(admission_head: &str, include_proof: bool) -> serde_json::Value {
679
- let proof_results = if include_proof {
680
- json!([
681
- {
682
- "checkId": "diff-check",
683
- "command": "git diff --check",
684
- "cwd": ".",
685
- "exitCode": 0,
686
- "checkedAt": "2026-05-06T00:00:00.000Z",
687
- "evidence": ["README.md"]
688
- }
689
- ])
690
- } else {
691
- json!([])
692
- };
693
-
694
- json!({
695
- "schema": "naome.task-state.v1",
696
- "version": 1,
697
- "status": "complete",
698
- "activeTask": {
699
- "id": "readme-task",
700
- "request": "Change README.",
701
- "userPrompt": {
702
- "receivedAt": "2026-05-06T00:00:00.000Z",
703
- "text": "Change README."
704
- },
705
- "admission": {
706
- "command": "node .naome/bin/check-task-state.js --admission",
707
- "cwd": ".",
708
- "exitCode": 0,
709
- "checkedAt": "2026-05-06T00:00:00.000Z",
710
- "gitHead": admission_head,
711
- "changedPaths": []
712
- },
713
- "allowedPaths": ["README.md"],
714
- "declaredChangeTypes": ["product-docs"],
715
- "requiredCheckIds": ["diff-check"],
716
- "proofResults": proof_results,
717
- "revisions": [],
718
- "humanReview": {
719
- "required": false,
720
- "approved": false,
721
- "reason": null
722
- }
723
- },
724
- "blocker": null,
725
- "updatedAt": "2026-05-06T00:00:00.000Z"
726
- })
727
- }
728
-
729
- struct TestRepo {
730
- root: PathBuf,
731
- }
732
-
733
- impl TestRepo {
734
- fn new(name: &str) -> Self {
735
- let nonce = SystemTime::now()
736
- .duration_since(UNIX_EPOCH)
737
- .unwrap()
738
- .as_nanos();
739
- let root = std::env::temp_dir().join(format!("naome-intent-{name}-{nonce}"));
740
- fs::create_dir_all(root.join(".naome")).unwrap();
741
- Self { root }
742
- }
743
-
744
- fn path(&self) -> &Path {
745
- &self.root
746
- }
747
-
748
- fn write_file(&self, relative_path: &str, content: &str) {
749
- let path = self.root.join(relative_path);
750
- if let Some(parent) = path.parent() {
751
- fs::create_dir_all(parent).unwrap();
752
- }
753
- fs::write(path, content).unwrap();
754
- }
755
-
756
- fn write_base_naome_state(&self, task_state: serde_json::Value) {
757
- self.write_naome_json(
758
- "init-state.json",
759
- json!({ "initialized": true, "intakeStatus": "complete" }),
760
- );
761
- self.write_naome_json("upgrade-state.json", json!({ "status": "complete" }));
762
- self.write_naome_json(
763
- "verification.json",
764
- json!({
765
- "schema": "naome.verification.v1",
766
- "version": 1,
767
- "status": "ready",
768
- "checks": [
769
- {
770
- "id": "diff-check",
771
- "command": "git diff --check",
772
- "cwd": ".",
773
- "purpose": "Reject whitespace errors.",
774
- "cost": "fast",
775
- "source": "test",
776
- "evidence": ["README.md"],
777
- "lastVerified": null
778
- }
779
- ],
780
- "changeTypes": [],
781
- "releaseGates": []
782
- }),
783
- );
784
- self.write_naome_json("task-state.json", task_state);
785
- }
786
-
787
- fn write_naome_json(&self, file_name: &str, value: serde_json::Value) {
788
- let path = self.root.join(".naome").join(file_name);
789
- fs::write(
790
- path,
791
- format!("{}\n", serde_json::to_string_pretty(&value).unwrap()),
792
- )
793
- .unwrap();
794
- }
795
-
796
- fn init_git(&self) {
797
- self.git(&["init"]);
798
- self.git(&["config", "user.email", "naome@example.com"]);
799
- self.git(&["config", "user.name", "NAOME Test"]);
800
- }
801
-
802
- fn git(&self, args: &[&str]) {
803
- let output = Command::new("git")
804
- .args(args)
805
- .current_dir(&self.root)
806
- .output()
807
- .unwrap();
808
- assert!(
809
- output.status.success(),
810
- "git {:?} failed\nstdout:\n{}\nstderr:\n{}",
811
- args,
812
- String::from_utf8_lossy(&output.stdout),
813
- String::from_utf8_lossy(&output.stderr)
814
- );
815
- }
816
-
817
- fn git_stdout(&self, args: &[&str]) -> String {
818
- let output = Command::new("git")
819
- .args(args)
820
- .current_dir(&self.root)
821
- .output()
822
- .unwrap();
823
- assert!(output.status.success());
824
- String::from_utf8_lossy(&output.stdout).trim().to_string()
825
- }
126
+ fn assert_intent(intent: &IntentDecision, state: &str, prompt: &str, policy: &str, allowed: bool) {
127
+ assert_eq!(intent.repo_state, state);
128
+ assert_eq!(intent.prompt_intent, prompt);
129
+ assert_eq!(intent.policy_action, policy);
130
+ assert_eq!(intent.allowed, allowed);
826
131
  }