@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,167 @@
1
+ use super::resolver_shared::Policy;
2
+
3
+ pub(crate) const POLICY_ACTIVE_BLOCKED: Policy = (
4
+ "block_unowned_diff",
5
+ false,
6
+ "The active task is blocked by out-of-scope changes.",
7
+ "Resolve the scope blocker before interpreting new work.",
8
+ );
9
+ pub(crate) const POLICY_ACTIVE_CANCEL: Policy = (
10
+ "cancel_current_task",
11
+ false,
12
+ "The prompt asks to cancel the active task.",
13
+ "Cancel or review the active task before starting another.",
14
+ );
15
+ pub(crate) const POLICY_ACTIVE_CONTINUE: Policy = (
16
+ "continue_current_task",
17
+ true,
18
+ "A task is active and the prompt appears to continue or finish it.",
19
+ "Continue the current task and keep proof current.",
20
+ );
21
+ pub(crate) const POLICY_ACTIVE_NEW_BLOCK: Policy = (
22
+ "block_ambiguous_intent",
23
+ false,
24
+ "A task is already active and the prompt appears to ask for a distinct new goal.",
25
+ "Complete, revise, or cancel the active task before starting another.",
26
+ );
27
+ pub(crate) const POLICY_ACTIVE_NO_COMMIT: Policy = (
28
+ "continue_current_task_without_commit",
29
+ true,
30
+ "A task is active and the prompt blocks committing.",
31
+ "Continue the active task without baselining.",
32
+ );
33
+ pub(crate) const POLICY_ACTIVE_REVIEW: Policy = (
34
+ "review_current_task_diff",
35
+ false,
36
+ "The prompt asks to review the active task diff.",
37
+ "Review the active task before mutating it further.",
38
+ );
39
+ pub(crate) const POLICY_BASELINE_BLOCK: Policy = (
40
+ "block_unowned_diff",
41
+ false,
42
+ "Setup, upgrade, or repair changes must be resolved before task routing.",
43
+ "Resolve the harness baseline decision before accepting feature work.",
44
+ );
45
+ pub(crate) const POLICY_BASELINE_CANCEL: Policy = (
46
+ "cancel_upgrade_baseline",
47
+ false,
48
+ "The prompt explicitly asks to cancel setup, upgrade, or repair changes.",
49
+ "Cancel or review the NAOME diff before feature work.",
50
+ );
51
+ pub(crate) const POLICY_BASELINE_COMMIT_UPGRADE: Policy = (
52
+ "commit_upgrade_baseline",
53
+ true,
54
+ "The prompt asks to baseline pending setup, upgrade, or repair changes.",
55
+ "Run the appropriate NAOME baseline flow before feature work.",
56
+ );
57
+ pub(crate) const POLICY_BASELINE_NEW_REFRESH: Policy = ("auto_commit_harness_refresh_then_create_new_task", true, "The prompt asks for a new goal and the only blocker is a deterministic harness refresh.", "Baseline the harness refresh automatically, rerun admission, then create the new task in the same worktree.");
58
+ pub(crate) const POLICY_BASELINE_NEW_UPGRADE: Policy = ("auto_commit_upgrade_baseline_then_create_new_task", true, "The prompt asks for a new goal and the only blocker is a completed NAOME setup, upgrade, or repair diff.", "Baseline the NAOME diff automatically, rerun admission, then create the new task.");
59
+ pub(crate) const POLICY_BASELINE_NO_COMMIT: Policy = ("block_auto_baseline_due_to_no_commit", false, "The prompt explicitly blocks committing, so NAOME will not auto-baseline setup or repair changes.", "Review, cancel, or manually resolve the setup diff before feature work.");
60
+ pub(crate) const POLICY_BASELINE_REVIEW: Policy = (
61
+ "review_diff_first",
62
+ false,
63
+ "The prompt explicitly asks to review setup, upgrade, or repair changes.",
64
+ "Review the NAOME diff before any baseline or new task.",
65
+ );
66
+ pub(crate) const POLICY_COMPLETED_CANCEL: Policy = (
67
+ "cancel_task_changes",
68
+ false,
69
+ "The prompt explicitly asks to cancel the completed task changes.",
70
+ "Cancel or review the completed task diff before any new task.",
71
+ );
72
+ pub(crate) const POLICY_COMPLETED_COMMIT: Policy = (
73
+ "commit_task_baseline",
74
+ true,
75
+ "The prompt asks to baseline the completed task diff.",
76
+ "Run the NAOME commit baseline flow for the completed task.",
77
+ );
78
+ pub(crate) const POLICY_COMPLETED_NEW_INVALID: Policy = (
79
+ "block_unsafe_intent",
80
+ false,
81
+ "The prompt asks for a new task, but the current completed task is not safe to auto-baseline.",
82
+ "Review the completed diff and proof before starting new work.",
83
+ );
84
+ pub(crate) const POLICY_COMPLETED_NEW_ISOLATED: Policy = ("auto_commit_completed_task_then_create_isolated_task_worktree", true, "The completed task is valid, and unrelated user edits are present.", "Baseline only the completed task paths, preserve unrelated user edits, and create an isolated worktree for the next task.");
85
+ pub(crate) const POLICY_COMPLETED_NEW_REFRESH: Policy = ("auto_commit_harness_refresh_then_completed_task_then_create_new_task", true, "The completed task is valid after separating deterministic harness refresh changes.", "Baseline the harness refresh first, then baseline the completed task and create the next task.");
86
+ pub(crate) const POLICY_COMPLETED_NEW_VALID: Policy = (
87
+ "auto_commit_completed_task_then_create_new_task",
88
+ true,
89
+ "The completed task is valid and the prompt asks for a distinct new goal.",
90
+ "Baseline the completed task first, then create the next task.",
91
+ );
92
+ pub(crate) const POLICY_COMPLETED_NO_COMMIT: Policy = (
93
+ "block_auto_baseline_due_to_no_commit",
94
+ false,
95
+ "The prompt explicitly blocks committing, so NAOME will not auto-baseline the completed task.",
96
+ "Review, revise, cancel, or manually resolve the completed task diff.",
97
+ );
98
+ pub(crate) const POLICY_COMPLETED_REVIEW: Policy = (
99
+ "review_task_diff",
100
+ false,
101
+ "The prompt explicitly asks to review the completed task diff.",
102
+ "Review the completed task diff before any baseline or new task.",
103
+ );
104
+ pub(crate) const POLICY_COMPLETED_REVISION: Policy = (
105
+ "reopen_completed_task_revision",
106
+ true,
107
+ "The prompt refers to follow-up correction on the unbaselined completed task.",
108
+ "Reopen the same task as a revision and mark proof stale before editing.",
109
+ );
110
+ pub(crate) const POLICY_DIRTY_BLOCK: Policy = ("block_unowned_diff", false, "The repository has unowned changes that must be resolved first.", "NAOME will not commit unowned changes. Review them, or route a new task so NAOME can isolate task work in a separate worktree.");
111
+ pub(crate) const POLICY_DIRTY_COMMIT_USER: Policy = ("commit_user_diff_with_quality_gate", true, "The prompt asks to commit unowned user changes.", "Run NAOME's user-diff quality gate, then commit only the current changed paths if every required check passes.");
112
+ pub(crate) const POLICY_DIRTY_NEW_ISOLATED: Policy = ("auto_commit_harness_refresh_then_create_isolated_task_worktree", true, "The repository has deterministic harness refresh changes plus unrelated user edits.", "Baseline only the harness refresh first, then create an isolated task worktree without touching user edits.");
113
+ pub(crate) const POLICY_DIRTY_NEW_REFRESH: Policy = (
114
+ "auto_commit_harness_refresh_then_create_new_task",
115
+ true,
116
+ "The repository has only deterministic harness refresh changes.",
117
+ "Baseline the harness refresh first, then create the next task in the same worktree.",
118
+ );
119
+ pub(crate) const POLICY_DIRTY_NEW_WORKTREE: Policy = ("create_isolated_task_worktree", true, "The repository has unrelated user changes, and the prompt asks for a new task.", "Create an isolated NAOME task worktree so the new task can proceed without touching those user changes.");
120
+ pub(crate) const POLICY_FIRST_RUN: Policy = (
121
+ "run_first_run_protocol",
122
+ false,
123
+ "First-run intake must finish before feature work.",
124
+ "Run the NAOME first-run protocol.",
125
+ );
126
+ pub(crate) const POLICY_HARNESS_UNHEALTHY: Policy = (
127
+ "repair_harness_only",
128
+ false,
129
+ "The harness is unhealthy, so normal task work is blocked.",
130
+ "Repair or review the harness before accepting feature work.",
131
+ );
132
+ pub(crate) const POLICY_READY_COMMIT_BLOCK: Policy = (
133
+ "block_ambiguous_intent",
134
+ false,
135
+ "The repository is already ready for work; there is no open task diff to baseline.",
136
+ "Ask for the next concrete task or inspect status.",
137
+ );
138
+ pub(crate) const POLICY_READY_NEW: Policy = (
139
+ "create_new_task",
140
+ true,
141
+ "The repository is clean and the prompt asks for a new goal.",
142
+ "Create a NAOME task for the new request.",
143
+ );
144
+ pub(crate) const POLICY_READY_NO_COMMIT: Policy = (
145
+ "create_new_task_without_auto_baseline",
146
+ true,
147
+ "The repository is clean, so no commit is needed before the new task.",
148
+ "Create a NAOME task for the new request.",
149
+ );
150
+ pub(crate) const POLICY_READY_REVIEW_STATUS: Policy = (
151
+ "answer_status_only",
152
+ true,
153
+ "The prompt asks for review, but there is no open task diff.",
154
+ "Answer from the current NAOME status without editing files.",
155
+ );
156
+ pub(crate) const POLICY_READY_REVISION_BLOCK: Policy = (
157
+ "block_ambiguous_intent",
158
+ false,
159
+ "The prompt sounds like a correction, but there is no open task diff.",
160
+ "Ask whether this is a new task or a status question.",
161
+ );
162
+ pub(crate) const POLICY_UPGRADE: Policy = (
163
+ "run_upgrade_protocol",
164
+ false,
165
+ "A NAOME upgrade is pending before feature work.",
166
+ "Run the NAOME upgrade protocol.",
167
+ );
@@ -0,0 +1,72 @@
1
+ use crate::models::Decision;
2
+
3
+ use super::model::IntentKind;
4
+ use super::resolver::{CompletedTaskReadiness, DirtyDiffReadiness};
5
+ use super::resolver_active::active_policy;
6
+ use super::resolver_baseline::{baseline_policy, dirty_policy};
7
+ use super::resolver_catalog::{
8
+ POLICY_ACTIVE_BLOCKED, POLICY_COMPLETED_CANCEL, POLICY_COMPLETED_COMMIT,
9
+ POLICY_COMPLETED_NEW_INVALID, POLICY_COMPLETED_NEW_ISOLATED, POLICY_COMPLETED_NEW_REFRESH,
10
+ POLICY_COMPLETED_NEW_VALID, POLICY_COMPLETED_NO_COMMIT, POLICY_COMPLETED_REVIEW,
11
+ POLICY_COMPLETED_REVISION, POLICY_FIRST_RUN, POLICY_HARNESS_UNHEALTHY,
12
+ POLICY_READY_COMMIT_BLOCK, POLICY_READY_NEW, POLICY_READY_NO_COMMIT,
13
+ POLICY_READY_REVIEW_STATUS, POLICY_READY_REVISION_BLOCK, POLICY_UPGRADE,
14
+ };
15
+ use super::resolver_shared::{ambiguous_policy, Policy};
16
+
17
+ pub(crate) fn state_policy(
18
+ decision: &Decision,
19
+ prompt_intent: IntentKind,
20
+ completed: CompletedTaskReadiness,
21
+ dirty: DirtyDiffReadiness,
22
+ risky: bool,
23
+ ) -> Policy {
24
+ match decision.state.as_str() {
25
+ "harness_unhealthy" => POLICY_HARNESS_UNHEALTHY,
26
+ "first_run_required" => POLICY_FIRST_RUN,
27
+ "upgrade_required" => POLICY_UPGRADE,
28
+ "ready_for_task" => ready_policy(prompt_intent),
29
+ "completed_task_unbaselined" => completed_policy(prompt_intent, completed, risky),
30
+ "active_task_in_progress" => active_policy(prompt_intent),
31
+ "active_task_blocked" => POLICY_ACTIVE_BLOCKED,
32
+ "dirty_unowned_diff" | "task_admission_blocked" => {
33
+ dirty_policy(prompt_intent, dirty, risky)
34
+ }
35
+ "install_or_upgrade_unbaselined" | "harness_repair_unbaselined" => {
36
+ baseline_policy(prompt_intent, dirty, risky)
37
+ }
38
+ _ => ambiguous_policy(),
39
+ }
40
+ }
41
+
42
+ fn ready_policy(intent: IntentKind) -> Policy {
43
+ match intent {
44
+ IntentKind::NewTask => POLICY_READY_NEW,
45
+ IntentKind::CommitRequest => POLICY_READY_COMMIT_BLOCK,
46
+ IntentKind::ReviewRequest => POLICY_READY_REVIEW_STATUS,
47
+ IntentKind::NoCommitRequest => POLICY_READY_NO_COMMIT,
48
+ IntentKind::TaskRevision => POLICY_READY_REVISION_BLOCK,
49
+ _ => ambiguous_policy(),
50
+ }
51
+ }
52
+
53
+ fn completed_policy(intent: IntentKind, completed: CompletedTaskReadiness, risky: bool) -> Policy {
54
+ match intent {
55
+ IntentKind::NoCommitRequest => POLICY_COMPLETED_NO_COMMIT,
56
+ IntentKind::ReviewRequest => POLICY_COMPLETED_REVIEW,
57
+ IntentKind::CancelRequest => POLICY_COMPLETED_CANCEL,
58
+ IntentKind::CommitRequest => POLICY_COMPLETED_COMMIT,
59
+ IntentKind::TaskRevision => POLICY_COMPLETED_REVISION,
60
+ IntentKind::NewTask => completed_new_task_policy(completed, risky),
61
+ _ => ambiguous_policy(),
62
+ }
63
+ }
64
+
65
+ fn completed_new_task_policy(completed: CompletedTaskReadiness, risky: bool) -> Policy {
66
+ match (completed, risky) {
67
+ (CompletedTaskReadiness::Valid, false) => POLICY_COMPLETED_NEW_VALID,
68
+ (CompletedTaskReadiness::ValidAfterHarnessRefresh, false) => POLICY_COMPLETED_NEW_REFRESH,
69
+ (CompletedTaskReadiness::ValidWithUnrelatedDirty, false) => POLICY_COMPLETED_NEW_ISOLATED,
70
+ _ => POLICY_COMPLETED_NEW_INVALID,
71
+ }
72
+ }
@@ -0,0 +1,55 @@
1
+ pub(crate) type Policy = (&'static str, bool, &'static str, &'static str);
2
+
3
+ pub(crate) fn policy(
4
+ action: &'static str,
5
+ allowed: bool,
6
+ summary: &'static str,
7
+ next_action: &'static str,
8
+ ) -> Policy {
9
+ (action, allowed, summary, next_action)
10
+ }
11
+
12
+ pub(crate) fn harness_refresh_policy() -> Policy {
13
+ policy(
14
+ "auto_commit_harness_refresh_baseline",
15
+ true,
16
+ "The repository has only deterministic harness refresh changes.",
17
+ "Baseline the harness refresh before feature work.",
18
+ )
19
+ }
20
+
21
+ pub(crate) fn ambiguous_empty() -> Policy {
22
+ policy(
23
+ "block_ambiguous_intent",
24
+ false,
25
+ "The prompt is empty, so NAOME cannot infer a safe task transition.",
26
+ "Ask for a concrete request before mutating the repository.",
27
+ )
28
+ }
29
+
30
+ pub(crate) fn ambiguous_conflict() -> Policy {
31
+ policy(
32
+ "block_ambiguous_intent",
33
+ false,
34
+ "The prompt contains conflicting workflow actions.",
35
+ "Ask which workflow action should run before mutating the repository.",
36
+ )
37
+ }
38
+
39
+ pub(crate) fn ambiguous_policy() -> Policy {
40
+ policy(
41
+ "block_ambiguous_intent",
42
+ false,
43
+ "The prompt does not clearly map to a safe NAOME task transition.",
44
+ "Ask for a narrower request or inspect status.",
45
+ )
46
+ }
47
+
48
+ pub(crate) fn unsafe_policy() -> Policy {
49
+ policy(
50
+ "block_unsafe_intent",
51
+ false,
52
+ "The prompt contains risky terms or asks to bypass guardrails.",
53
+ "Ask for a narrower request that does not expose secrets or bypass guardrails.",
54
+ )
55
+ }
@@ -0,0 +1,40 @@
1
+ use super::model::{PromptSegment, RiskContext, SegmentKind};
2
+ use super::patterns::lexical_tokens;
3
+
4
+ pub(crate) fn detect_risk(segments: &[PromptSegment]) -> RiskContext {
5
+ let mut codes = Vec::new();
6
+ for segment in segments
7
+ .iter()
8
+ .filter(|segment| segment.kind == SegmentKind::CommandLike)
9
+ {
10
+ let tokens = lexical_tokens(&segment.text);
11
+ if is_bypass_command(&tokens) {
12
+ codes.push("structured_risk:bypass_command".to_string());
13
+ }
14
+ if is_publish_command(&tokens) {
15
+ codes.push("structured_risk:publish_command".to_string());
16
+ }
17
+ }
18
+
19
+ codes.sort();
20
+ codes.dedup();
21
+ RiskContext {
22
+ has_risky_terms: !codes.is_empty(),
23
+ risk_codes: codes,
24
+ }
25
+ }
26
+
27
+ fn is_bypass_command(tokens: &[String]) -> bool {
28
+ tokens
29
+ .iter()
30
+ .any(|token| matches!(token.as_str(), "--no-verify" | "no-verify"))
31
+ || tokens
32
+ .windows(2)
33
+ .any(|window| window[0] == "git" && window[1] == "push")
34
+ }
35
+
36
+ fn is_publish_command(tokens: &[String]) -> bool {
37
+ tokens
38
+ .windows(2)
39
+ .any(|window| window[0] == "npm" && window[1] == "publish")
40
+ }
@@ -0,0 +1,170 @@
1
+ use super::model::{PromptSegment, SegmentKind};
2
+
3
+ pub(crate) fn segment_prompt(prompt: &str) -> Vec<PromptSegment> {
4
+ let mut segments = Vec::new();
5
+ let mut rest = prompt;
6
+
7
+ loop {
8
+ let Some(start) = rest.find("```") else {
9
+ segment_plain_block(&mut segments, rest);
10
+ break;
11
+ };
12
+
13
+ segment_plain_block(&mut segments, &rest[..start]);
14
+ let after_open = &rest[start + 3..];
15
+ let Some(end) = after_open.find("```") else {
16
+ push_segment(&mut segments, SegmentKind::CodeFence, after_open);
17
+ break;
18
+ };
19
+
20
+ push_segment(
21
+ &mut segments,
22
+ SegmentKind::CodeFence,
23
+ code_fence_body(&after_open[..end]),
24
+ );
25
+ rest = &after_open[end + 3..];
26
+ }
27
+
28
+ segments
29
+ }
30
+
31
+ fn segment_plain_block(segments: &mut Vec<PromptSegment>, text: &str) {
32
+ for line in text.lines() {
33
+ let trimmed = line.trim();
34
+ if !trimmed.is_empty() {
35
+ segment_line(segments, line);
36
+ }
37
+ }
38
+ }
39
+
40
+ fn code_fence_body(text: &str) -> &str {
41
+ if let Some((language, body)) = text.split_once('\n') {
42
+ if language.trim().chars().all(|ch| ch.is_ascii_alphanumeric()) {
43
+ return body;
44
+ }
45
+ }
46
+ text
47
+ }
48
+
49
+ pub(crate) fn actionable_text(segments: &[PromptSegment]) -> String {
50
+ segments
51
+ .iter()
52
+ .filter(|segment| {
53
+ matches!(
54
+ segment.kind,
55
+ SegmentKind::Prose | SegmentKind::ListItem | SegmentKind::CommandLike
56
+ )
57
+ })
58
+ .map(|segment| segment.text.as_str())
59
+ .collect::<Vec<_>>()
60
+ .join(" ")
61
+ }
62
+
63
+ fn segment_line(segments: &mut Vec<PromptSegment>, line: &str) {
64
+ let trimmed = line.trim();
65
+ if trimmed.is_empty() {
66
+ return;
67
+ }
68
+
69
+ let (kind, text) = if let Some(item) = list_item_text(trimmed) {
70
+ (SegmentKind::ListItem, item)
71
+ } else if is_command_like(trimmed) {
72
+ (SegmentKind::CommandLike, trimmed)
73
+ } else if is_path_like(trimmed) {
74
+ (SegmentKind::FilePath, trimmed)
75
+ } else {
76
+ (SegmentKind::Prose, trimmed)
77
+ };
78
+
79
+ segment_inline(segments, kind, text);
80
+ }
81
+
82
+ fn segment_inline(segments: &mut Vec<PromptSegment>, default_kind: SegmentKind, text: &str) {
83
+ let mut rest = text;
84
+ loop {
85
+ let Some(start) = rest.find('`') else {
86
+ segment_quotes(segments, default_kind, rest);
87
+ break;
88
+ };
89
+ segment_quotes(segments, default_kind, &rest[..start]);
90
+ let after_tick = &rest[start + 1..];
91
+ let Some(end) = after_tick.find('`') else {
92
+ segment_quotes(segments, default_kind, rest);
93
+ break;
94
+ };
95
+ push_segment(segments, SegmentKind::InlineCode, &after_tick[..end]);
96
+ rest = &after_tick[end + 1..];
97
+ }
98
+ }
99
+
100
+ fn segment_quotes(segments: &mut Vec<PromptSegment>, default_kind: SegmentKind, text: &str) {
101
+ let mut current = String::new();
102
+ let mut quoted = String::new();
103
+ let mut in_quote = false;
104
+ for ch in text.chars() {
105
+ if matches!(ch, '"' | '\'' | '“' | '”' | '‘' | '’') {
106
+ if in_quote {
107
+ push_segment(segments, SegmentKind::QuotedText, &quoted);
108
+ quoted.clear();
109
+ } else {
110
+ push_segment(segments, default_kind, &current);
111
+ current.clear();
112
+ }
113
+ in_quote = !in_quote;
114
+ } else if in_quote {
115
+ quoted.push(ch);
116
+ } else {
117
+ current.push(ch);
118
+ }
119
+ }
120
+
121
+ let kind = if in_quote {
122
+ default_kind
123
+ } else {
124
+ SegmentKind::QuotedText
125
+ };
126
+ push_segment(segments, kind, &quoted);
127
+ push_segment(segments, default_kind, &current);
128
+ }
129
+
130
+ fn push_segment(segments: &mut Vec<PromptSegment>, kind: SegmentKind, text: &str) {
131
+ let trimmed = text.trim();
132
+ if !trimmed.is_empty() {
133
+ segments.push(PromptSegment {
134
+ kind,
135
+ text: trimmed.to_string(),
136
+ });
137
+ }
138
+ }
139
+
140
+ fn list_item_text(trimmed: &str) -> Option<&str> {
141
+ if let Some(rest) = trimmed
142
+ .strip_prefix("- ")
143
+ .or_else(|| trimmed.strip_prefix("* "))
144
+ {
145
+ return Some(rest.trim());
146
+ }
147
+
148
+ let (number, rest) = trimmed.split_once(". ")?;
149
+ number
150
+ .chars()
151
+ .all(|ch| ch.is_ascii_digit())
152
+ .then_some(rest.trim())
153
+ }
154
+
155
+ fn is_command_like(trimmed: &str) -> bool {
156
+ let first = trimmed.split_whitespace().next().unwrap_or_default();
157
+ matches!(
158
+ first,
159
+ "git" | "npm" | "node" | "cargo" | "naome" | "rg" | "python" | "python3"
160
+ )
161
+ }
162
+
163
+ fn is_path_like(trimmed: &str) -> bool {
164
+ !trimmed.contains(' ')
165
+ && (trimmed.contains('/')
166
+ || trimmed.ends_with(".rs")
167
+ || trimmed.ends_with(".js")
168
+ || trimmed.ends_with(".json")
169
+ || trimmed.ends_with(".md"))
170
+ }