@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
@@ -0,0 +1,123 @@
1
+ use std::path::Path;
2
+
3
+ use serde_json::Value;
4
+
5
+ use crate::models::NaomeError;
6
+
7
+ use super::diff::validate_changed_paths;
8
+ use super::git_io::read_git_changed_paths;
9
+ use super::human_review_state::validate_human_review_state;
10
+ use super::reconcile::format_dirty_diff_admission_blocker;
11
+ use super::shape::{
12
+ format_blocker, validate_active_task, validate_active_task_references, validate_blocker,
13
+ validate_pending_upgrade,
14
+ };
15
+ use super::util::{joined_strings, read_json};
16
+ pub(super) fn validate_progress(
17
+ task_state: &Value,
18
+ root: &Path,
19
+ errors: &mut Vec<String>,
20
+ ) -> Result<(), NaomeError> {
21
+ let status = checked_status(task_state, root, errors)?;
22
+
23
+ match status {
24
+ "planning" | "implementing" | "revising" | "verifying" => {
25
+ validate_active_task(task_state.get("activeTask"), errors);
26
+ validate_pending_upgrade(task_state, root, errors)?;
27
+ validate_active_task_references(
28
+ task_state.get("activeTask"),
29
+ root,
30
+ errors,
31
+ Some(status),
32
+ )?;
33
+ if let Some(active_task) = task_state.get("activeTask") {
34
+ validate_changed_paths(active_task, root, errors)?;
35
+ }
36
+ }
37
+ "needs_human_review" => {
38
+ validate_human_review_state(task_state, root, errors)?;
39
+ errors.push(format_blocker(
40
+ "Human review required",
41
+ task_state.get("blocker"),
42
+ ));
43
+ }
44
+ "blocked" => {
45
+ validate_active_task(task_state.get("activeTask"), errors);
46
+ validate_blocker(task_state.get("blocker"), errors);
47
+ errors.push(format_blocker("Task is blocked", task_state.get("blocker")));
48
+ }
49
+ "complete" => errors.push(
50
+ "Task is complete; use node .naome/bin/check-task-state.js for completion validation."
51
+ .to_string(),
52
+ ),
53
+ "idle" => errors.push(
54
+ "No active task is in progress; use --admission before starting feature work."
55
+ .to_string(),
56
+ ),
57
+ _ => {}
58
+ }
59
+
60
+ Ok(())
61
+ }
62
+
63
+ pub(super) fn checked_status<'a>(
64
+ task_state: &'a Value,
65
+ root: &Path,
66
+ errors: &mut Vec<String>,
67
+ ) -> Result<&'a str, NaomeError> {
68
+ validate_init_complete(root, errors)?;
69
+ validate_upgrade_complete(root, errors)?;
70
+ Ok(task_state
71
+ .get("status")
72
+ .and_then(Value::as_str)
73
+ .unwrap_or("invalid"))
74
+ }
75
+ pub(super) fn validate_upgrade_complete(
76
+ root: &Path,
77
+ errors: &mut Vec<String>,
78
+ ) -> Result<(), NaomeError> {
79
+ let Some(upgrade_state) = read_json(root, ".naome/upgrade-state.json", errors)? else {
80
+ return Ok(());
81
+ };
82
+
83
+ if upgrade_state.get("status").and_then(Value::as_str) != Some("complete") {
84
+ let pending = joined_strings(upgrade_state.get("pending"), "unknown");
85
+ errors.push(format!("NAOME upgrade is not complete. Finish docs/naome/upgrade.md before feature work. Pending: {pending}"));
86
+ }
87
+ Ok(())
88
+ }
89
+
90
+ pub(super) fn validate_init_complete(
91
+ root: &Path,
92
+ errors: &mut Vec<String>,
93
+ ) -> Result<(), NaomeError> {
94
+ let Some(init_state) = read_json(root, ".naome/init-state.json", errors)? else {
95
+ return Ok(());
96
+ };
97
+
98
+ if init_state.get("initialized").and_then(Value::as_bool) != Some(true)
99
+ || init_state.get("intakeStatus").and_then(Value::as_str) != Some("complete")
100
+ {
101
+ errors.push(
102
+ "NAOME intake is not complete. Finish docs/naome/first-run.md before feature work."
103
+ .to_string(),
104
+ );
105
+ }
106
+ Ok(())
107
+ }
108
+
109
+ pub(super) fn validate_clean_git_diff(
110
+ task_state: &Value,
111
+ root: &Path,
112
+ errors: &mut Vec<String>,
113
+ ) -> Result<(), NaomeError> {
114
+ let changed_paths = read_git_changed_paths(root)?;
115
+ if !changed_paths.is_empty() {
116
+ errors.push(format_dirty_diff_admission_blocker(
117
+ task_state,
118
+ root,
119
+ &changed_paths,
120
+ )?);
121
+ }
122
+ Ok(())
123
+ }
@@ -0,0 +1,139 @@
1
+ use std::collections::HashSet;
2
+ use std::path::Path;
3
+
4
+ use serde_json::Value;
5
+
6
+ use crate::models::NaomeError;
7
+
8
+ use super::diff::task_diff_from_entries;
9
+ pub(super) use super::evidence::{
10
+ evidence_entry_path, validate_control_state_paths, validate_control_state_patterns,
11
+ validate_evidence_array, validate_evidence_paths,
12
+ };
13
+ use super::git_io::read_git_changed_entries;
14
+ use super::proof_entry::validate_proof_result;
15
+ use super::proof_model::canonical_proofs;
16
+ use super::types::ChangedEntry;
17
+ use super::util::{is_iso_datetime, matches_any_pattern, normalize_path, string_array};
18
+ pub(super) fn validate_proof_result_entries(
19
+ active_task: &Value,
20
+ check_ids: &HashSet<String>,
21
+ root: &Path,
22
+ errors: &mut Vec<String>,
23
+ ) -> Result<(), NaomeError> {
24
+ if let Some(proofs) = active_task.get("proofResults").and_then(Value::as_array) {
25
+ for (index, proof) in proofs.iter().enumerate() {
26
+ validate_proof_result(proof, index, check_ids, root, errors, active_task)?;
27
+ }
28
+ }
29
+ for proof in canonical_proofs(active_task, root, errors)? {
30
+ if proof.command.trim().is_empty() {
31
+ errors.push(format!(
32
+ "activeTask proof command is empty: {}",
33
+ proof.check_id
34
+ ));
35
+ }
36
+ if proof.cwd.trim().is_empty() {
37
+ errors.push(format!("activeTask proof cwd is empty: {}", proof.check_id));
38
+ }
39
+ if !is_iso_datetime(&proof.checked_at) {
40
+ errors.push(format!(
41
+ "activeTask proof checkedAt must be an ISO timestamp: {}",
42
+ proof.check_id
43
+ ));
44
+ }
45
+ }
46
+
47
+ Ok(())
48
+ }
49
+
50
+ pub(super) fn validate_proof_results(
51
+ active_task: &Value,
52
+ check_ids: &HashSet<String>,
53
+ root: &Path,
54
+ errors: &mut Vec<String>,
55
+ ) -> Result<(), NaomeError> {
56
+ let Some(required_check_ids) = active_task
57
+ .get("requiredCheckIds")
58
+ .and_then(Value::as_array)
59
+ else {
60
+ return Ok(());
61
+ };
62
+ let proofs = canonical_proofs(active_task, root, errors)?;
63
+
64
+ for check_id in required_check_ids {
65
+ let Some(check_id) = check_id.as_str().filter(|value| !value.trim().is_empty()) else {
66
+ continue;
67
+ };
68
+
69
+ match proofs.iter().find(|proof| proof.check_id == check_id) {
70
+ Some(proof) if proof.exit_code == 0 => {}
71
+ Some(_) => errors.push(format!(
72
+ "activeTask.proofResults failed proof result: {check_id}"
73
+ )),
74
+ None => errors.push(format!(
75
+ "activeTask.proofResults missing proof result: {check_id}"
76
+ )),
77
+ }
78
+ }
79
+
80
+ validate_proof_result_entries(active_task, check_ids, root, errors)
81
+ }
82
+
83
+ pub(super) fn validate_proof_evidence_covers_changed_paths(
84
+ active_task: Option<&Value>,
85
+ root: &Path,
86
+ errors: &mut Vec<String>,
87
+ ) -> Result<(), NaomeError> {
88
+ let Some(active_task) = active_task else {
89
+ return Ok(());
90
+ };
91
+ let mut proof_errors = Vec::new();
92
+ let proofs = canonical_proofs(active_task, root, &mut proof_errors)?;
93
+ if proofs.is_empty() {
94
+ return Ok(());
95
+ }
96
+
97
+ let entries = read_git_changed_entries(root)?;
98
+ validate_proof_evidence_covers_changed_entries(active_task, root, &entries, errors)
99
+ }
100
+
101
+ pub(super) fn validate_proof_evidence_covers_changed_entries(
102
+ active_task: &Value,
103
+ root: &Path,
104
+ entries: &[ChangedEntry],
105
+ errors: &mut Vec<String>,
106
+ ) -> Result<(), NaomeError> {
107
+ let mut proof_errors = Vec::new();
108
+ let proofs = canonical_proofs(active_task, root, &mut proof_errors)?;
109
+ if proofs.is_empty() {
110
+ return Ok(());
111
+ }
112
+
113
+ let changed_paths = task_diff_from_entries(active_task, entries);
114
+ let evidence_paths: HashSet<String> = proofs
115
+ .iter()
116
+ .flat_map(|proof| proof.evidence.clone())
117
+ .filter_map(|entry| evidence_entry_path(&entry).map(normalize_path))
118
+ .collect();
119
+
120
+ let allowed_paths = string_array(active_task.get("allowedPaths")).unwrap_or_default();
121
+ let changed_allowed_paths: Vec<String> = changed_paths
122
+ .diff_paths
123
+ .into_iter()
124
+ .filter(|path| matches_any_pattern(path, &allowed_paths))
125
+ .collect();
126
+ let missing_paths: Vec<String> = changed_allowed_paths
127
+ .into_iter()
128
+ .filter(|path| !evidence_paths.contains(path))
129
+ .collect();
130
+
131
+ if !missing_paths.is_empty() {
132
+ errors.push(format!(
133
+ "activeTask.proofResults evidence missing changed allowed paths: {}",
134
+ missing_paths.join(", ")
135
+ ));
136
+ }
137
+
138
+ Ok(())
139
+ }
@@ -0,0 +1,66 @@
1
+ use std::collections::HashSet;
2
+ use std::path::Path;
3
+
4
+ use serde_json::Value;
5
+
6
+ use crate::models::NaomeError;
7
+
8
+ use super::proof::{
9
+ validate_control_state_paths, validate_evidence_array, validate_evidence_paths,
10
+ };
11
+ use super::util::{is_iso_datetime, require_string};
12
+
13
+ pub(super) fn validate_proof_result(
14
+ proof: &Value,
15
+ index: usize,
16
+ check_ids: &HashSet<String>,
17
+ root: &Path,
18
+ errors: &mut Vec<String>,
19
+ active_task: &Value,
20
+ ) -> Result<(), NaomeError> {
21
+ let prefix = format!("activeTask.proofResults[{index}]");
22
+ let Some(object) = proof.as_object() else {
23
+ errors.push(format!("{prefix} must be an object."));
24
+ return Ok(());
25
+ };
26
+
27
+ require_string(object.get("checkId"), &format!("{prefix}.checkId"), errors);
28
+ require_string(object.get("command"), &format!("{prefix}.command"), errors);
29
+ require_string(object.get("cwd"), &format!("{prefix}.cwd"), errors);
30
+ validate_evidence_array(
31
+ object.get("evidence"),
32
+ &format!("{prefix}.evidence"),
33
+ errors,
34
+ );
35
+ validate_control_state_paths(
36
+ object.get("evidence"),
37
+ &format!("{prefix}.evidence"),
38
+ errors,
39
+ );
40
+
41
+ if object.get("exitCode").and_then(Value::as_i64).is_none() {
42
+ errors.push(format!("{prefix}.exitCode must be an integer."));
43
+ }
44
+
45
+ if !object
46
+ .get("checkedAt")
47
+ .and_then(Value::as_str)
48
+ .is_some_and(is_iso_datetime)
49
+ {
50
+ errors.push(format!("{prefix}.checkedAt must be an ISO timestamp."));
51
+ }
52
+
53
+ if let Some(check_id) = object.get("checkId").and_then(Value::as_str) {
54
+ if !check_id.trim().is_empty() && !check_ids.contains(check_id) {
55
+ errors.push(format!("{prefix}.checkId unknown check id: {check_id}"));
56
+ }
57
+ }
58
+
59
+ validate_evidence_paths(
60
+ object.get("evidence"),
61
+ &format!("{prefix}.evidence"),
62
+ root,
63
+ errors,
64
+ active_task,
65
+ )
66
+ }
@@ -0,0 +1,70 @@
1
+ use std::path::Path;
2
+
3
+ use serde_json::Value;
4
+
5
+ use crate::models::NaomeError;
6
+
7
+ use super::compact_proof::compact_proofs;
8
+ use super::proof_sources::{check_id_from_proof, read_verification_defaults};
9
+
10
+ #[derive(Debug, Clone)]
11
+ pub(super) struct CanonicalProof {
12
+ pub(super) check_id: String,
13
+ pub(super) command: String,
14
+ pub(super) cwd: String,
15
+ pub(super) exit_code: i64,
16
+ pub(super) checked_at: String,
17
+ pub(super) evidence: Vec<Value>,
18
+ }
19
+
20
+ #[derive(Debug, Clone)]
21
+ pub(super) struct VerificationDefaults {
22
+ pub(super) command: String,
23
+ pub(super) cwd: String,
24
+ }
25
+
26
+ pub(super) fn canonical_proofs(
27
+ active_task: &Value,
28
+ root: &Path,
29
+ errors: &mut Vec<String>,
30
+ ) -> Result<Vec<CanonicalProof>, NaomeError> {
31
+ let defaults = read_verification_defaults(root, errors)?;
32
+ let mut proofs = legacy_proofs(active_task);
33
+ proofs.extend(compact_proofs(active_task, root, errors, &defaults)?);
34
+ Ok(proofs)
35
+ }
36
+
37
+ pub(crate) fn canonical_proof_check_ids(active_task: &Value) -> Vec<String> {
38
+ let mut check_ids = Vec::new();
39
+ if let Some(proofs) = active_task.get("proofResults").and_then(Value::as_array) {
40
+ check_ids.extend(proofs.iter().filter_map(check_id_from_proof));
41
+ }
42
+ if let Some(batches) = active_task.get("proofBatches").and_then(Value::as_array) {
43
+ for batch in batches {
44
+ if let Some(proofs) = batch.get("proofs").and_then(Value::as_array) {
45
+ check_ids.extend(proofs.iter().filter_map(check_id_from_proof));
46
+ }
47
+ }
48
+ }
49
+ check_ids
50
+ }
51
+
52
+ fn legacy_proofs(active_task: &Value) -> Vec<CanonicalProof> {
53
+ active_task
54
+ .get("proofResults")
55
+ .and_then(Value::as_array)
56
+ .into_iter()
57
+ .flatten()
58
+ .filter_map(|proof| {
59
+ let evidence = proof.get("evidence")?.as_array()?.clone();
60
+ Some(CanonicalProof {
61
+ check_id: proof.get("checkId")?.as_str()?.to_string(),
62
+ command: proof.get("command")?.as_str()?.to_string(),
63
+ cwd: proof.get("cwd")?.as_str()?.to_string(),
64
+ exit_code: proof.get("exitCode")?.as_i64()?,
65
+ checked_at: proof.get("checkedAt")?.as_str()?.to_string(),
66
+ evidence,
67
+ })
68
+ })
69
+ .collect()
70
+ }
@@ -0,0 +1,76 @@
1
+ use std::collections::HashMap;
2
+ use std::path::Path;
3
+
4
+ use serde_json::Value;
5
+
6
+ use crate::models::NaomeError;
7
+
8
+ use super::proof::{
9
+ validate_control_state_paths, validate_evidence_array, validate_evidence_paths,
10
+ };
11
+ use super::proof_model::VerificationDefaults;
12
+ use super::util::read_json;
13
+
14
+ pub(super) fn read_path_sets(
15
+ active_task: &Value,
16
+ root: &Path,
17
+ errors: &mut Vec<String>,
18
+ ) -> Result<HashMap<String, Vec<Value>>, NaomeError> {
19
+ let mut sets = HashMap::new();
20
+ let Some(path_sets) = active_task.get("proofPathSets") else {
21
+ return Ok(sets);
22
+ };
23
+ let Some(path_sets) = path_sets.as_object() else {
24
+ errors.push("activeTask.proofPathSets must be an object when present.".to_string());
25
+ return Ok(sets);
26
+ };
27
+ for (name, value) in path_sets {
28
+ let prefix = format!("activeTask.proofPathSets.{name}");
29
+ validate_evidence_array(Some(value), &prefix, errors);
30
+ validate_control_state_paths(Some(value), &prefix, errors);
31
+ validate_evidence_paths(Some(value), &prefix, root, errors, active_task)?;
32
+ if let Some(paths) = value.as_array() {
33
+ sets.insert(name.clone(), paths.clone());
34
+ }
35
+ }
36
+ Ok(sets)
37
+ }
38
+
39
+ pub(super) fn read_verification_defaults(
40
+ root: &Path,
41
+ errors: &mut Vec<String>,
42
+ ) -> Result<HashMap<String, VerificationDefaults>, NaomeError> {
43
+ let Some(verification) = read_json(root, ".naome/verification.json", errors)? else {
44
+ return Ok(HashMap::new());
45
+ };
46
+ let mut defaults = HashMap::new();
47
+ for check in verification
48
+ .get("checks")
49
+ .and_then(Value::as_array)
50
+ .into_iter()
51
+ .flatten()
52
+ {
53
+ let (Some(id), Some(command), Some(cwd)) = (
54
+ check.get("id").and_then(Value::as_str),
55
+ check.get("command").and_then(Value::as_str),
56
+ check.get("cwd").and_then(Value::as_str),
57
+ ) else {
58
+ continue;
59
+ };
60
+ defaults.insert(
61
+ id.to_string(),
62
+ VerificationDefaults {
63
+ command: command.to_string(),
64
+ cwd: cwd.to_string(),
65
+ },
66
+ );
67
+ }
68
+ Ok(defaults)
69
+ }
70
+
71
+ pub(super) fn check_id_from_proof(proof: &Value) -> Option<String> {
72
+ proof
73
+ .get("checkId")
74
+ .and_then(Value::as_str)
75
+ .map(ToString::to_string)
76
+ }
@@ -0,0 +1,49 @@
1
+ use std::path::Path;
2
+
3
+ use serde_json::Value;
4
+
5
+ use crate::models::NaomeError;
6
+
7
+ use super::repair::{is_completed_task_diff, is_harness_repair_diff, is_naome_baseline_diff};
8
+ use super::shape::validate_blocker;
9
+ use super::types::BLOCKING_STATUS;
10
+ pub(super) fn validate_push_gate(task_state: &Value, errors: &mut Vec<String>) {
11
+ let status = task_state
12
+ .get("status")
13
+ .and_then(Value::as_str)
14
+ .unwrap_or("invalid");
15
+ if !BLOCKING_STATUS.contains(&status) {
16
+ return;
17
+ }
18
+
19
+ if status == "blocked" || status == "needs_human_review" {
20
+ validate_blocker(task_state.get("blocker"), errors);
21
+ }
22
+
23
+ errors.push(format!("NAOME push gate blocked because task state is {status}. Resolve the active task before pushing. Human options: continue_current_task, request_task_changes, mark_task_blocked, cancel_task_state."));
24
+ }
25
+
26
+ pub(super) fn format_dirty_diff_admission_blocker(
27
+ task_state: &Value,
28
+ root: &Path,
29
+ changed_paths: &[String],
30
+ ) -> Result<String, NaomeError> {
31
+ let prefix = format!(
32
+ "Task admission requires a clean git diff. Changed paths: {}.",
33
+ changed_paths.join(", ")
34
+ );
35
+
36
+ if is_harness_repair_diff(root, changed_paths)? {
37
+ return Ok(format!("{prefix} These look like completed Harness Repair changes. Run NAOME intent for the next natural-language request before deciding whether to baseline, review, or cancel the repair diff."));
38
+ }
39
+
40
+ if is_completed_task_diff(task_state, changed_paths) {
41
+ return Ok(format!("{prefix} These look like completed task changes. Run NAOME intent for the next natural-language request; deterministic policy can baseline a valid completed task before creating the next task."));
42
+ }
43
+
44
+ if is_naome_baseline_diff(changed_paths) {
45
+ return Ok(format!("{prefix} These look like completed NAOME install or upgrade changes. Run NAOME intent for the next natural-language request; deterministic policy can baseline setup before creating the next task."));
46
+ }
47
+
48
+ Ok(format!("{prefix} Ask the user to choose exactly one: review_task_diff, request_task_changes, cancel_task_changes. Do not start new feature work or commit without explicit user selection."))
49
+ }
@@ -0,0 +1,7 @@
1
+ pub(super) use super::completed_refresh::add_completed_task_diff_notice;
2
+ pub(super) use super::push_gate::{format_dirty_diff_admission_blocker, validate_push_gate};
3
+ pub(super) use super::repair::{
4
+ is_deterministic_harness_refresh_diff, is_harness_repair_diff,
5
+ is_install_or_upgrade_baseline_diff, is_packaged_machine_owned_path, is_repair_archive_path,
6
+ is_safe_harness_refresh_path,
7
+ };