@lamentis/naome 1.2.0 → 1.3.0

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 (139) hide show
  1. package/Cargo.lock +2 -2
  2. package/README.md +108 -47
  3. package/bin/naome-node.js +2 -1579
  4. package/bin/naome.js +34 -5
  5. package/crates/naome-cli/Cargo.toml +1 -1
  6. package/crates/naome-cli/src/dispatcher.rs +7 -2
  7. package/crates/naome-cli/src/main.rs +37 -22
  8. package/crates/naome-cli/src/quality_commands.rs +317 -10
  9. package/crates/naome-cli/src/workflow_commands.rs +21 -1
  10. package/crates/naome-core/Cargo.toml +1 -1
  11. package/crates/naome-core/src/decision/checks.rs +64 -0
  12. package/crates/naome-core/src/decision/idle.rs +67 -0
  13. package/crates/naome-core/src/decision/json.rs +36 -0
  14. package/crates/naome-core/src/decision/states.rs +165 -0
  15. package/crates/naome-core/src/decision.rs +131 -353
  16. package/crates/naome-core/src/git.rs +4 -2
  17. package/crates/naome-core/src/install_plan.rs +4 -0
  18. package/crates/naome-core/src/lib.rs +12 -6
  19. package/crates/naome-core/src/paths.rs +3 -1
  20. package/crates/naome-core/src/quality/adapter_support.rs +89 -0
  21. package/crates/naome-core/src/quality/adapters.rs +20 -67
  22. package/crates/naome-core/src/quality/baseline.rs +8 -0
  23. package/crates/naome-core/src/quality/cache.rs +153 -0
  24. package/crates/naome-core/src/quality/checks/duplicate_blocks.rs +25 -11
  25. package/crates/naome-core/src/quality/checks/near_duplicates.rs +4 -2
  26. package/crates/naome-core/src/quality/checks.rs +7 -8
  27. package/crates/naome-core/src/quality/cleanup.rs +48 -3
  28. package/crates/naome-core/src/quality/config.rs +8 -15
  29. package/crates/naome-core/src/quality/config_support.rs +24 -0
  30. package/crates/naome-core/src/quality/mod.rs +72 -6
  31. package/crates/naome-core/src/quality/scanner/analysis/normalize.rs +78 -0
  32. package/crates/naome-core/src/quality/scanner/analysis.rs +160 -0
  33. package/crates/naome-core/src/quality/scanner/repo_paths.rs +39 -3
  34. package/crates/naome-core/src/quality/scanner.rs +200 -215
  35. package/crates/naome-core/src/quality/semantic/checks.rs +134 -0
  36. package/crates/naome-core/src/quality/semantic/extract.rs +158 -0
  37. package/crates/naome-core/src/quality/semantic/model.rs +85 -0
  38. package/crates/naome-core/src/quality/semantic/route.rs +52 -0
  39. package/crates/naome-core/src/quality/semantic.rs +68 -0
  40. package/crates/naome-core/src/quality/structure/adapters.rs +84 -0
  41. package/crates/naome-core/src/quality/structure/checks/basic.rs +153 -0
  42. package/crates/naome-core/src/quality/structure/checks/directory.rs +134 -0
  43. package/crates/naome-core/src/quality/structure/checks/pairing.rs +63 -0
  44. package/crates/naome-core/src/quality/structure/checks.rs +124 -0
  45. package/crates/naome-core/src/quality/structure/classify/roles.rs +188 -0
  46. package/crates/naome-core/src/quality/structure/classify.rs +146 -0
  47. package/crates/naome-core/src/quality/structure/config.rs +89 -0
  48. package/crates/naome-core/src/quality/structure/defaults.rs +107 -0
  49. package/crates/naome-core/src/quality/structure/mod.rs +77 -0
  50. package/crates/naome-core/src/quality/structure/model.rs +131 -0
  51. package/crates/naome-core/src/quality/types.rs +43 -2
  52. package/crates/naome-core/src/route/builtin_checks.rs +141 -0
  53. package/crates/naome-core/src/route/builtin_context.rs +73 -0
  54. package/crates/naome-core/src/route/builtin_integrity.rs +49 -0
  55. package/crates/naome-core/src/route/builtin_require.rs +40 -0
  56. package/crates/naome-core/src/route/context.rs +180 -0
  57. package/crates/naome-core/src/route/execution.rs +96 -0
  58. package/crates/naome-core/src/route/execution_baselines.rs +146 -0
  59. package/crates/naome-core/src/route/execution_support.rs +57 -0
  60. package/crates/naome-core/src/route/execution_tasks.rs +71 -0
  61. package/crates/naome-core/src/route/git_ops.rs +72 -0
  62. package/crates/naome-core/src/route/quality_gate.rs +73 -0
  63. package/crates/naome-core/src/route/quality_gate_config.rs +126 -0
  64. package/crates/naome-core/src/route/quality_gate_snapshot.rs +69 -0
  65. package/crates/naome-core/src/route/worktree.rs +75 -0
  66. package/crates/naome-core/src/route/worktree_files.rs +32 -0
  67. package/crates/naome-core/src/route/worktree_plan.rs +131 -0
  68. package/crates/naome-core/src/route.rs +44 -1217
  69. package/crates/naome-core/src/verification.rs +1 -0
  70. package/crates/naome-core/src/workflow/doctor.rs +144 -0
  71. package/crates/naome-core/src/workflow/mod.rs +2 -0
  72. package/crates/naome-core/src/workflow/mutation.rs +1 -2
  73. package/crates/naome-core/tests/decision.rs +24 -118
  74. package/crates/naome-core/tests/harness_health.rs +2 -0
  75. package/crates/naome-core/tests/install_plan.rs +2 -0
  76. package/crates/naome-core/tests/quality.rs +26 -123
  77. package/crates/naome-core/tests/quality_performance.rs +231 -0
  78. package/crates/naome-core/tests/quality_structure.rs +116 -0
  79. package/crates/naome-core/tests/quality_structure_adapters.rs +98 -0
  80. package/crates/naome-core/tests/quality_structure_policy.rs +144 -0
  81. package/crates/naome-core/tests/quality_structure_support/mod.rs +249 -0
  82. package/crates/naome-core/tests/repo_support/mod.rs +16 -0
  83. package/crates/naome-core/tests/repo_support/repo.rs +113 -0
  84. package/crates/naome-core/tests/repo_support/repo_factories.rs +99 -0
  85. package/crates/naome-core/tests/repo_support/repo_helpers.rs +123 -0
  86. package/crates/naome-core/tests/repo_support/routes.rs +81 -0
  87. package/crates/naome-core/tests/repo_support/verification.rs +168 -0
  88. package/crates/naome-core/tests/repo_support/verification_values.rs +135 -0
  89. package/crates/naome-core/tests/route.rs +1 -1376
  90. package/crates/naome-core/tests/route_baseline.rs +86 -0
  91. package/crates/naome-core/tests/route_completion.rs +141 -0
  92. package/crates/naome-core/tests/route_harness_refresh.rs +135 -0
  93. package/crates/naome-core/tests/route_user_diff.rs +202 -0
  94. package/crates/naome-core/tests/route_worktree.rs +54 -0
  95. package/crates/naome-core/tests/semantic_legacy.rs +140 -0
  96. package/crates/naome-core/tests/task_state.rs +60 -432
  97. package/crates/naome-core/tests/task_state_compact_support/repo.rs +1 -1
  98. package/crates/naome-core/tests/task_state_support/mod.rs +163 -0
  99. package/crates/naome-core/tests/task_state_support/states.rs +84 -0
  100. package/crates/naome-core/tests/verification.rs +4 -45
  101. package/crates/naome-core/tests/verification_contract.rs +22 -78
  102. package/crates/naome-core/tests/workflow_doctor.rs +24 -0
  103. package/crates/naome-core/tests/workflow_policy.rs +6 -1
  104. package/crates/naome-core/tests/workflow_support/mod.rs +1 -1
  105. package/installer/agents.js +90 -0
  106. package/installer/context.js +67 -0
  107. package/installer/filesystem.js +166 -0
  108. package/installer/flows.js +84 -0
  109. package/installer/git-boundary.js +171 -0
  110. package/installer/git-hook-content.js +36 -0
  111. package/installer/git-hooks.js +134 -0
  112. package/installer/git-local.js +2 -0
  113. package/installer/git-shared.js +35 -0
  114. package/installer/harness-file-ops.js +140 -0
  115. package/installer/harness-files.js +56 -0
  116. package/installer/harness-verification.js +123 -0
  117. package/installer/install-plan.js +66 -0
  118. package/installer/main.js +25 -0
  119. package/installer/manifest-state.js +167 -0
  120. package/installer/native-build.js +24 -0
  121. package/installer/native-format.js +6 -0
  122. package/installer/native.js +162 -0
  123. package/installer/output.js +131 -0
  124. package/installer/version.js +32 -0
  125. package/native/darwin-arm64/naome +0 -0
  126. package/native/linux-x64/naome +0 -0
  127. package/package.json +2 -1
  128. package/templates/naome-root/.naome/bin/check-harness-health.js +3 -3
  129. package/templates/naome-root/.naome/bin/check-task-state.js +3 -3
  130. package/templates/naome-root/.naome/bin/naome.js +32 -21
  131. package/templates/naome-root/.naome/manifest.json +5 -3
  132. package/templates/naome-root/.naome/repository-structure.json +90 -0
  133. package/templates/naome-root/.naome/verification.json +1 -0
  134. package/templates/naome-root/.naomeignore +1 -0
  135. package/templates/naome-root/docs/naome/agent-workflow.md +16 -14
  136. package/templates/naome-root/docs/naome/index.md +4 -3
  137. package/templates/naome-root/docs/naome/repository-quality.md +66 -4
  138. package/templates/naome-root/docs/naome/repository-structure.md +51 -0
  139. package/templates/naome-root/docs/naome/testing.md +2 -1
@@ -0,0 +1,49 @@
1
+ use std::collections::HashMap;
2
+
3
+ use crate::models::NaomeError;
4
+
5
+ pub(super) fn packaged_harness_integrity() -> Result<HashMap<String, String>, NaomeError> {
6
+ const CHECKER: &str =
7
+ include_str!("../../../../templates/naome-root/.naome/bin/check-harness-health.js");
8
+ let start_marker = "const expectedMachineOwnedIntegrity = Object.freeze({";
9
+ let start = CHECKER
10
+ .find(start_marker)
11
+ .ok_or_else(|| NaomeError::new("Packaged harness integrity block is missing."))?;
12
+ let body_start = start + start_marker.len();
13
+ let end = CHECKER[body_start..]
14
+ .find("\n});")
15
+ .map(|offset| body_start + offset)
16
+ .ok_or_else(|| NaomeError::new("Packaged harness integrity block is incomplete."))?;
17
+
18
+ let mut integrity = HashMap::new();
19
+ for line in CHECKER[body_start..end].lines() {
20
+ let line = line.trim().trim_end_matches(',').trim();
21
+ if line.is_empty() {
22
+ continue;
23
+ }
24
+ let Some((path, hash)) = line.split_once(':') else {
25
+ return Err(NaomeError::new(format!(
26
+ "Packaged harness integrity entry is invalid: {line}"
27
+ )));
28
+ };
29
+ let path: String = serde_json::from_str(path.trim()).map_err(|error| {
30
+ NaomeError::new(format!(
31
+ "Packaged harness integrity path is invalid: {error}"
32
+ ))
33
+ })?;
34
+ let hash: String = serde_json::from_str(hash.trim()).map_err(|error| {
35
+ NaomeError::new(format!(
36
+ "Packaged harness integrity hash is invalid: {error}"
37
+ ))
38
+ })?;
39
+ integrity.insert(path, hash);
40
+ }
41
+
42
+ if integrity.is_empty() {
43
+ return Err(NaomeError::new(
44
+ "Packaged harness integrity block is empty.",
45
+ ));
46
+ }
47
+
48
+ Ok(integrity)
49
+ }
@@ -0,0 +1,40 @@
1
+ use crate::models::NaomeError;
2
+
3
+ use super::quality_gate::QualityCheck;
4
+
5
+ pub(super) fn require_builtin_quality_check_any(
6
+ check_id: &str,
7
+ check: &QualityCheck,
8
+ expected_commands: &[&str],
9
+ ) -> Result<(), NaomeError> {
10
+ if check.cwd == "."
11
+ && expected_commands
12
+ .iter()
13
+ .any(|expected_command| check.command == *expected_command)
14
+ {
15
+ return Ok(());
16
+ }
17
+
18
+ Err(NaomeError::new(format!(
19
+ "Quality check {check_id} has an unsafe command or cwd; expected one of [{}] with cwd `.`.",
20
+ expected_commands
21
+ .iter()
22
+ .map(|command| format!("`{command}`"))
23
+ .collect::<Vec<_>>()
24
+ .join(", ")
25
+ )))
26
+ }
27
+
28
+ pub(super) fn require_builtin_quality_check(
29
+ check_id: &str,
30
+ check: &QualityCheck,
31
+ expected_command: &str,
32
+ ) -> Result<(), NaomeError> {
33
+ if check.cwd == "." && check.command == expected_command {
34
+ return Ok(());
35
+ }
36
+
37
+ Err(NaomeError::new(format!(
38
+ "Quality check {check_id} has an unsafe command or cwd; expected command `{expected_command}` with cwd `.`."
39
+ )))
40
+ }
@@ -0,0 +1,180 @@
1
+ use crate::intent::IntentDecision;
2
+ use crate::models::Decision;
3
+
4
+ pub(super) fn can_create_task(intent: &IntentDecision, decision: &Decision, execute: bool) -> bool {
5
+ let creation_intent = matches!(
6
+ intent.policy_action.as_str(),
7
+ "create_new_task"
8
+ | "create_new_task_without_auto_baseline"
9
+ | "auto_commit_completed_task_then_create_new_task"
10
+ | "auto_commit_completed_task_then_create_isolated_task_worktree"
11
+ | "auto_commit_harness_refresh_then_completed_task_then_create_new_task"
12
+ | "auto_commit_harness_refresh_then_create_new_task"
13
+ | "auto_commit_harness_refresh_then_create_isolated_task_worktree"
14
+ | "auto_commit_upgrade_baseline_then_create_new_task"
15
+ | "create_isolated_task_worktree"
16
+ );
17
+ if !creation_intent || decision.state != "ready_for_task" || decision.blocked {
18
+ return false;
19
+ }
20
+
21
+ execute
22
+ || matches!(
23
+ intent.policy_action.as_str(),
24
+ "create_new_task" | "create_new_task_without_auto_baseline"
25
+ )
26
+ }
27
+
28
+ pub(super) fn winning_rule(intent: &IntentDecision) -> String {
29
+ match intent.policy_action.as_str() {
30
+ "block_unsafe_intent" => "unsafe_intent_precedence",
31
+ "block_auto_baseline_due_to_no_commit" => "no_commit_blocks_auto_baseline",
32
+ "continue_current_task_without_commit" => "no_commit_continues_active_task",
33
+ "review_task_diff" | "review_diff_first" | "review_current_task_diff" => {
34
+ "explicit_review_overrides_auto_baseline"
35
+ }
36
+ "cancel_task_changes" | "cancel_upgrade_baseline" | "cancel_current_task" => {
37
+ "explicit_cancel_overrides_auto_baseline"
38
+ }
39
+ "auto_commit_completed_task_then_create_new_task" => {
40
+ "completed_task_valid_new_task_auto_baseline"
41
+ }
42
+ "auto_commit_completed_task_then_create_isolated_task_worktree" => {
43
+ "completed_task_valid_with_unrelated_dirty_worktree"
44
+ }
45
+ "auto_commit_harness_refresh_then_completed_task_then_create_new_task" => {
46
+ "completed_task_harness_refresh_split_auto_baseline"
47
+ }
48
+ "auto_commit_harness_refresh_then_create_new_task" => {
49
+ "pure_harness_refresh_new_task_auto_baseline"
50
+ }
51
+ "auto_commit_harness_refresh_then_create_isolated_task_worktree" => {
52
+ "dirty_repo_harness_refresh_worktree_isolation"
53
+ }
54
+ "auto_commit_harness_refresh_baseline" => "dirty_repo_harness_refresh_repair_baseline",
55
+ "auto_commit_upgrade_baseline_then_create_new_task" => "setup_diff_new_task_auto_baseline",
56
+ "reopen_completed_task_revision" | "continue_current_task" => {
57
+ "current_task_revision_continues_task"
58
+ }
59
+ "answer_status_only" => "status_request_read_only",
60
+ "create_new_task" | "create_new_task_without_auto_baseline" => "ready_repo_new_task",
61
+ "create_isolated_task_worktree" => "dirty_repo_new_task_worktree_isolation",
62
+ "commit_user_diff_with_quality_gate" => "explicit_user_diff_commit_quality_gate",
63
+ _ => "fallback_policy",
64
+ }
65
+ .to_string()
66
+ }
67
+
68
+ pub(super) fn discarded_actions(intent: &IntentDecision) -> Vec<String> {
69
+ let Some(actions_note) = intent
70
+ .internal_notes
71
+ .iter()
72
+ .find(|note| note.starts_with("internal_allowed_actions:"))
73
+ else {
74
+ return Vec::new();
75
+ };
76
+ let selected = selected_internal_action(&intent.policy_action);
77
+ actions_note
78
+ .trim_start_matches("internal_allowed_actions:")
79
+ .split(',')
80
+ .filter(|action| !action.is_empty() && Some(*action) != selected.as_deref())
81
+ .map(ToString::to_string)
82
+ .collect()
83
+ }
84
+
85
+ fn selected_internal_action(policy_action: &str) -> Option<String> {
86
+ match policy_action {
87
+ "auto_commit_completed_task_then_create_new_task"
88
+ | "auto_commit_completed_task_then_create_isolated_task_worktree"
89
+ | "commit_task_baseline" => Some("commit_task_baseline".to_string()),
90
+ "auto_commit_harness_refresh_then_create_new_task"
91
+ | "auto_commit_harness_refresh_then_create_isolated_task_worktree"
92
+ | "auto_commit_harness_refresh_baseline" => Some("commit_upgrade_baseline".to_string()),
93
+ "auto_commit_harness_refresh_then_completed_task_then_create_new_task" => {
94
+ Some("commit_task_baseline".to_string())
95
+ }
96
+ "auto_commit_upgrade_baseline_then_create_new_task" | "commit_upgrade_baseline" => {
97
+ Some("commit_upgrade_baseline".to_string())
98
+ }
99
+ other if other.starts_with("review_") || other.starts_with("cancel_") => {
100
+ Some(other.to_string())
101
+ }
102
+ _ => None,
103
+ }
104
+ }
105
+
106
+ pub(super) fn would_mutate(intent: &IntentDecision) -> bool {
107
+ matches!(
108
+ intent.policy_action.as_str(),
109
+ "auto_commit_completed_task_then_create_new_task"
110
+ | "auto_commit_completed_task_then_create_isolated_task_worktree"
111
+ | "auto_commit_upgrade_baseline_then_create_new_task"
112
+ | "auto_commit_harness_refresh_then_completed_task_then_create_new_task"
113
+ | "auto_commit_harness_refresh_then_create_new_task"
114
+ | "auto_commit_harness_refresh_then_create_isolated_task_worktree"
115
+ | "auto_commit_harness_refresh_baseline"
116
+ | "create_isolated_task_worktree"
117
+ | "commit_task_baseline"
118
+ | "commit_upgrade_baseline"
119
+ | "commit_user_diff_with_quality_gate"
120
+ )
121
+ }
122
+
123
+ pub(super) fn required_context_for_route(
124
+ intent: &IntentDecision,
125
+ decision: &Decision,
126
+ ) -> Vec<String> {
127
+ let mut context = required_context_for_intent(intent);
128
+ if decision.state == "ready_for_task" {
129
+ push_unique(&mut context, "docs/naome/agent-workflow.md");
130
+ push_unique(&mut context, "docs/naome/testing.md");
131
+ push_unique(&mut context, ".naome/verification.json");
132
+ }
133
+ context
134
+ }
135
+
136
+ pub(super) fn required_context_for_intent(intent: &IntentDecision) -> Vec<String> {
137
+ let mut context = intent.required_context.clone();
138
+ match intent.policy_action.as_str() {
139
+ "create_new_task" | "create_new_task_without_auto_baseline" => {
140
+ push_unique(&mut context, "docs/naome/agent-workflow.md");
141
+ push_unique(&mut context, "docs/naome/testing.md");
142
+ push_unique(&mut context, ".naome/verification.json");
143
+ push_unique(&mut context, "docs/naome/architecture.md");
144
+ }
145
+ "create_isolated_task_worktree" => {
146
+ push_unique(&mut context, "docs/naome/agent-workflow.md");
147
+ push_unique(&mut context, "docs/naome/testing.md");
148
+ push_unique(&mut context, ".naome/verification.json");
149
+ push_unique(&mut context, "docs/naome/architecture.md");
150
+ }
151
+ "auto_commit_completed_task_then_create_new_task"
152
+ | "auto_commit_completed_task_then_create_isolated_task_worktree"
153
+ | "auto_commit_harness_refresh_then_create_new_task"
154
+ | "auto_commit_harness_refresh_then_create_isolated_task_worktree"
155
+ | "auto_commit_harness_refresh_baseline"
156
+ | "auto_commit_harness_refresh_then_completed_task_then_create_new_task"
157
+ | "reopen_completed_task_revision"
158
+ | "review_task_diff"
159
+ | "cancel_task_changes"
160
+ | "block_auto_baseline_due_to_no_commit" => {
161
+ push_unique(&mut context, "docs/naome/execution.md");
162
+ push_unique(&mut context, ".naome/task-state.json");
163
+ }
164
+ "answer_status_only" => {
165
+ push_unique(&mut context, "docs/naome/index.md");
166
+ }
167
+ "repair_harness_only" => {
168
+ push_unique(&mut context, ".naome/manifest.json");
169
+ push_unique(&mut context, "docs/naome/index.md");
170
+ }
171
+ _ => {}
172
+ }
173
+ context
174
+ }
175
+
176
+ fn push_unique(context: &mut Vec<String>, value: &str) {
177
+ if !context.iter().any(|entry| entry == value) {
178
+ context.push(value.to_string());
179
+ }
180
+ }
@@ -0,0 +1,96 @@
1
+ use std::path::{Path, PathBuf};
2
+
3
+ use crate::intent::IntentDecision;
4
+ use crate::journal::TaskJournalEntry;
5
+ use crate::models::{Decision, NaomeError};
6
+
7
+ use super::execution_baselines::{
8
+ baseline_completed_task, baseline_completed_task_then_worktree, baseline_harness_refresh,
9
+ baseline_harness_refresh_then_completed_task, baseline_harness_refresh_then_worktree,
10
+ baseline_pure_harness_refresh, baseline_setup, commit_upgrade_baseline,
11
+ };
12
+ use super::execution_tasks::{
13
+ commit_user_diff, create_task_worktree, journal_external_baseline_if_needed,
14
+ };
15
+ use super::RouteWorktree;
16
+
17
+ pub(super) struct RouteExecution {
18
+ pub(super) mutation_performed: bool,
19
+ pub(super) executed_actions: Vec<String>,
20
+ pub(super) journal_entry: Option<TaskJournalEntry>,
21
+ pub(super) user_message: String,
22
+ pub(super) task_root: PathBuf,
23
+ pub(super) worktree: Option<RouteWorktree>,
24
+ pub(super) route_allowed: bool,
25
+ pub(super) human_options: Vec<String>,
26
+ }
27
+
28
+ impl RouteExecution {
29
+ pub(super) fn from_intent(root: &Path, intent: &IntentDecision) -> Self {
30
+ Self {
31
+ mutation_performed: false,
32
+ executed_actions: Vec::new(),
33
+ journal_entry: None,
34
+ user_message: intent.user_message.clone(),
35
+ task_root: root.to_path_buf(),
36
+ worktree: None,
37
+ route_allowed: intent.allowed,
38
+ human_options: intent.human_options.clone(),
39
+ }
40
+ }
41
+
42
+ pub(super) fn mark_action(&mut self, action: &str) {
43
+ self.mutation_performed = true;
44
+ self.executed_actions.push(action.to_string());
45
+ }
46
+ }
47
+
48
+ pub(super) fn execute_route_policy(
49
+ root: &Path,
50
+ prompt: &str,
51
+ initial_decision: &Decision,
52
+ intent: &IntentDecision,
53
+ repo_state_before: &str,
54
+ execution: &mut RouteExecution,
55
+ ) -> Result<(), NaomeError> {
56
+ match intent.policy_action.as_str() {
57
+ "auto_commit_completed_task_then_create_new_task" => baseline_completed_task(
58
+ root,
59
+ "route_auto_baseline",
60
+ "NAOME baselined the completed task and is ready to create the next task.",
61
+ execution,
62
+ ),
63
+ "auto_commit_completed_task_then_create_isolated_task_worktree" => {
64
+ baseline_completed_task_then_worktree(root, prompt, execution)
65
+ }
66
+ "auto_commit_harness_refresh_then_completed_task_then_create_new_task" => {
67
+ baseline_harness_refresh_then_completed_task(root, execution)
68
+ }
69
+ "auto_commit_harness_refresh_then_create_isolated_task_worktree" => {
70
+ baseline_harness_refresh_then_worktree(root, prompt, execution)
71
+ }
72
+ "auto_commit_harness_refresh_then_create_new_task" => {
73
+ baseline_pure_harness_refresh(root, execution)
74
+ }
75
+ "auto_commit_harness_refresh_baseline" => baseline_harness_refresh(root, execution),
76
+ "auto_commit_upgrade_baseline_then_create_new_task" => baseline_setup(root, execution),
77
+ "commit_task_baseline" => baseline_completed_task(
78
+ root,
79
+ "naome_commit_baseline",
80
+ "NAOME baselined the completed task and is ready for the next request.",
81
+ execution,
82
+ ),
83
+ "commit_upgrade_baseline" => commit_upgrade_baseline(root, execution),
84
+ "create_isolated_task_worktree" => create_task_worktree(root, prompt, execution),
85
+ "commit_user_diff_with_quality_gate" => commit_user_diff(root, execution),
86
+ "create_new_task" | "create_new_task_without_auto_baseline" => {
87
+ journal_external_baseline_if_needed(
88
+ root,
89
+ initial_decision,
90
+ repo_state_before,
91
+ execution,
92
+ )
93
+ }
94
+ _ => Ok(()),
95
+ }
96
+ }
@@ -0,0 +1,146 @@
1
+ use std::path::Path;
2
+
3
+ use crate::models::NaomeError;
4
+ use crate::task_state::{
5
+ completed_task_harness_refresh_diff, harness_refresh_diff, harness_refresh_with_unrelated_diff,
6
+ };
7
+
8
+ use super::execution::RouteExecution;
9
+ use super::execution_support::{commit_completed_task, commit_harness_paths, set_created_worktree};
10
+ use super::git_ops::{git_add_all, git_commit, git_head};
11
+ use super::worktree::{
12
+ create_isolated_task_worktree_with_name_head, preflight_isolated_task_worktree,
13
+ task_worktree_name_head,
14
+ };
15
+
16
+ pub(super) fn baseline_completed_task(
17
+ root: &Path,
18
+ journal_action: &str,
19
+ message: &str,
20
+ execution: &mut RouteExecution,
21
+ ) -> Result<(), NaomeError> {
22
+ commit_completed_task(root, git_head(root)?, journal_action, execution)?;
23
+ execution.user_message = message.to_string();
24
+ Ok(())
25
+ }
26
+
27
+ pub(super) fn baseline_completed_task_then_worktree(
28
+ root: &Path,
29
+ prompt: &str,
30
+ execution: &mut RouteExecution,
31
+ ) -> Result<(), NaomeError> {
32
+ let name_head = task_worktree_name_head(root)?;
33
+ preflight_isolated_task_worktree(root, prompt, &name_head)?;
34
+ commit_completed_task(
35
+ root,
36
+ Some(name_head.clone()),
37
+ "route_auto_baseline",
38
+ execution,
39
+ )?;
40
+ let created = create_isolated_task_worktree_with_name_head(root, prompt, &name_head)?;
41
+ set_created_worktree(
42
+ root,
43
+ created,
44
+ "NAOME baselined the completed task and created an isolated task worktree",
45
+ execution,
46
+ );
47
+ Ok(())
48
+ }
49
+
50
+ pub(super) fn baseline_harness_refresh_then_completed_task(
51
+ root: &Path,
52
+ execution: &mut RouteExecution,
53
+ ) -> Result<(), NaomeError> {
54
+ let Some(split) = completed_task_harness_refresh_diff(root)? else {
55
+ return Err(NaomeError::new(
56
+ "Unable to split harness refresh paths from completed task diff.",
57
+ ));
58
+ };
59
+ commit_harness_paths(root, &split.harness_paths, execution)?;
60
+ baseline_completed_task(
61
+ root,
62
+ "route_auto_baseline",
63
+ "NAOME baselined the harness refresh and completed task, then admitted the next task.",
64
+ execution,
65
+ )
66
+ }
67
+
68
+ pub(super) fn baseline_harness_refresh_then_worktree(
69
+ root: &Path,
70
+ prompt: &str,
71
+ execution: &mut RouteExecution,
72
+ ) -> Result<(), NaomeError> {
73
+ let name_head = task_worktree_name_head(root)?;
74
+ preflight_isolated_task_worktree(root, prompt, &name_head)?;
75
+ let Some(split) = harness_refresh_with_unrelated_diff(root)? else {
76
+ return Err(NaomeError::new(
77
+ "Unable to split harness refresh paths from unrelated dirty paths.",
78
+ ));
79
+ };
80
+ commit_harness_paths(root, &split.harness_paths, execution)?;
81
+ let created = create_isolated_task_worktree_with_name_head(root, prompt, &name_head)?;
82
+ set_created_worktree(
83
+ root,
84
+ created,
85
+ "NAOME baselined the harness refresh and created an isolated task worktree",
86
+ execution,
87
+ );
88
+ Ok(())
89
+ }
90
+
91
+ pub(super) fn baseline_pure_harness_refresh(
92
+ root: &Path,
93
+ execution: &mut RouteExecution,
94
+ ) -> Result<(), NaomeError> {
95
+ let Some(diff) = harness_refresh_diff(root)? else {
96
+ return Err(NaomeError::new(
97
+ "Unable to find deterministic harness refresh paths.",
98
+ ));
99
+ };
100
+ if !diff.unrelated_paths.is_empty() {
101
+ return Err(NaomeError::new(
102
+ "Harness refresh baseline expected no unrelated dirty paths.",
103
+ ));
104
+ }
105
+ commit_harness_paths(root, &diff.harness_paths, execution)?;
106
+ execution.user_message =
107
+ "NAOME baselined the harness refresh and is ready to create the next task in the same worktree."
108
+ .to_string();
109
+ Ok(())
110
+ }
111
+
112
+ pub(super) fn baseline_harness_refresh(
113
+ root: &Path,
114
+ execution: &mut RouteExecution,
115
+ ) -> Result<(), NaomeError> {
116
+ let Some(split) = harness_refresh_diff(root)? else {
117
+ return Err(NaomeError::new(
118
+ "Unable to find deterministic harness refresh paths.",
119
+ ));
120
+ };
121
+ commit_harness_paths(root, &split.harness_paths, execution)?;
122
+ execution.user_message =
123
+ "NAOME baselined the deterministic harness refresh and left unrelated user edits untouched."
124
+ .to_string();
125
+ Ok(())
126
+ }
127
+
128
+ pub(super) fn baseline_setup(
129
+ root: &Path,
130
+ execution: &mut RouteExecution,
131
+ ) -> Result<(), NaomeError> {
132
+ commit_upgrade_baseline(root, execution)?;
133
+ execution.user_message =
134
+ "NAOME baselined the setup changes and is ready to create the next task.".to_string();
135
+ Ok(())
136
+ }
137
+
138
+ pub(super) fn commit_upgrade_baseline(
139
+ root: &Path,
140
+ execution: &mut RouteExecution,
141
+ ) -> Result<(), NaomeError> {
142
+ git_add_all(root)?;
143
+ git_commit(root, "chore(naome): baseline setup")?;
144
+ execution.mark_action("commit_upgrade_baseline");
145
+ Ok(())
146
+ }
@@ -0,0 +1,57 @@
1
+ use std::path::{Path, PathBuf};
2
+
3
+ use crate::journal::append_task_journal;
4
+ use crate::models::{Decision, NaomeError};
5
+
6
+ use super::execution::RouteExecution;
7
+ use super::git_ops::{git_add_completed_task_paths, git_commit, git_head, git_stage_only_paths};
8
+ use super::RouteWorktree;
9
+
10
+ pub(super) fn initial_decision_completed(decision: &Decision) -> bool {
11
+ decision
12
+ .task
13
+ .as_ref()
14
+ .map(|task| task.status.clone())
15
+ .as_deref()
16
+ .is_some_and(|status| status == "complete")
17
+ }
18
+
19
+ pub(super) fn commit_completed_task(
20
+ root: &Path,
21
+ before: Option<String>,
22
+ journal_action: &str,
23
+ execution: &mut RouteExecution,
24
+ ) -> Result<(), NaomeError> {
25
+ git_add_completed_task_paths(root)?;
26
+ git_commit(root, "chore(naome): baseline completed task")?;
27
+ execution.journal_entry = append_task_journal(root, journal_action, before, git_head(root)?)?;
28
+ execution.mark_action("commit_task_baseline");
29
+ Ok(())
30
+ }
31
+
32
+ pub(super) fn commit_harness_paths(
33
+ root: &Path,
34
+ paths: &[String],
35
+ execution: &mut RouteExecution,
36
+ ) -> Result<(), NaomeError> {
37
+ git_stage_only_paths(root, paths)?;
38
+ git_commit(root, "chore(naome): baseline harness refresh")?;
39
+ execution.mark_action("commit_harness_refresh_baseline");
40
+ Ok(())
41
+ }
42
+
43
+ pub(super) fn set_created_worktree(
44
+ root: &Path,
45
+ created: RouteWorktree,
46
+ prefix: &str,
47
+ execution: &mut RouteExecution,
48
+ ) {
49
+ execution.task_root = PathBuf::from(&created.path);
50
+ execution.mark_action("create_task_worktree");
51
+ execution.user_message = format!(
52
+ "{prefix} at {}. Continue the new task there; unrelated user edits remain untouched in the original worktree.",
53
+ created.path
54
+ );
55
+ debug_assert_eq!(created.source_root, root.to_string_lossy().to_string());
56
+ execution.worktree = Some(created);
57
+ }
@@ -0,0 +1,71 @@
1
+ use std::path::Path;
2
+
3
+ use crate::git;
4
+ use crate::journal::append_task_journal;
5
+ use crate::models::{Decision, NaomeError};
6
+
7
+ use super::execution::RouteExecution;
8
+ use super::execution_support::{initial_decision_completed, set_created_worktree};
9
+ use super::git_ops::{git_commit, git_head, git_stage_only_paths};
10
+ use super::quality_gate::run_user_diff_quality_gate;
11
+ use super::worktree::create_isolated_task_worktree;
12
+
13
+ pub(super) fn create_task_worktree(
14
+ root: &Path,
15
+ prompt: &str,
16
+ execution: &mut RouteExecution,
17
+ ) -> Result<(), NaomeError> {
18
+ let created = create_isolated_task_worktree(root, prompt)?;
19
+ set_created_worktree(
20
+ root,
21
+ created,
22
+ "NAOME created an isolated task worktree",
23
+ execution,
24
+ );
25
+ Ok(())
26
+ }
27
+
28
+ pub(super) fn commit_user_diff(
29
+ root: &Path,
30
+ execution: &mut RouteExecution,
31
+ ) -> Result<(), NaomeError> {
32
+ let paths = git::changed_paths(root)?;
33
+ execution
34
+ .executed_actions
35
+ .push("run_user_diff_quality_gate".to_string());
36
+ match run_user_diff_quality_gate(root, &paths) {
37
+ Ok(check_ids) => {
38
+ git_stage_only_paths(root, &paths)?;
39
+ git_commit(root, "chore(user): baseline verified user changes")?;
40
+ execution.mark_action("commit_user_diff");
41
+ execution.user_message = format!(
42
+ "NAOME quality gates passed ({}) and committed only the current user-owned changed paths.",
43
+ check_ids.join(", ")
44
+ );
45
+ }
46
+ Err(error) => {
47
+ execution.route_allowed = false;
48
+ execution.human_options = vec!["review_unowned_diff".to_string()];
49
+ execution.user_message =
50
+ format!("NAOME user-diff quality gate failed, so no commit was created. {error}");
51
+ }
52
+ }
53
+ Ok(())
54
+ }
55
+
56
+ pub(super) fn journal_external_baseline_if_needed(
57
+ root: &Path,
58
+ initial_decision: &Decision,
59
+ repo_state_before: &str,
60
+ execution: &mut RouteExecution,
61
+ ) -> Result<(), NaomeError> {
62
+ if repo_state_before != "ready_for_task" || !initial_decision_completed(initial_decision) {
63
+ return Ok(());
64
+ }
65
+ execution.journal_entry =
66
+ append_task_journal(root, "external_baseline", None, git_head(root)?)?;
67
+ if execution.journal_entry.is_some() {
68
+ execution.mark_action("journal_external_task_baseline");
69
+ }
70
+ Ok(())
71
+ }