@lamentis/naome 1.2.0 → 1.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (113) hide show
  1. package/Cargo.lock +2 -2
  2. package/bin/naome-node.js +2 -1579
  3. package/bin/naome.js +19 -5
  4. package/crates/naome-cli/Cargo.toml +1 -1
  5. package/crates/naome-cli/src/dispatcher.rs +2 -1
  6. package/crates/naome-cli/src/main.rs +3 -0
  7. package/crates/naome-cli/src/quality_commands.rs +90 -2
  8. package/crates/naome-core/Cargo.toml +1 -1
  9. package/crates/naome-core/src/decision/checks.rs +64 -0
  10. package/crates/naome-core/src/decision/idle.rs +67 -0
  11. package/crates/naome-core/src/decision/json.rs +36 -0
  12. package/crates/naome-core/src/decision/states.rs +165 -0
  13. package/crates/naome-core/src/decision.rs +131 -353
  14. package/crates/naome-core/src/install_plan.rs +2 -0
  15. package/crates/naome-core/src/lib.rs +5 -3
  16. package/crates/naome-core/src/paths.rs +3 -1
  17. package/crates/naome-core/src/quality/adapter_support.rs +89 -0
  18. package/crates/naome-core/src/quality/adapters.rs +20 -67
  19. package/crates/naome-core/src/quality/cleanup.rs +13 -1
  20. package/crates/naome-core/src/quality/config.rs +8 -15
  21. package/crates/naome-core/src/quality/config_support.rs +24 -0
  22. package/crates/naome-core/src/quality/mod.rs +18 -0
  23. package/crates/naome-core/src/quality/scanner.rs +20 -8
  24. package/crates/naome-core/src/quality/structure/adapters.rs +84 -0
  25. package/crates/naome-core/src/quality/structure/checks/basic.rs +153 -0
  26. package/crates/naome-core/src/quality/structure/checks/directory.rs +144 -0
  27. package/crates/naome-core/src/quality/structure/checks/pairing.rs +63 -0
  28. package/crates/naome-core/src/quality/structure/checks.rs +124 -0
  29. package/crates/naome-core/src/quality/structure/classify/roles.rs +188 -0
  30. package/crates/naome-core/src/quality/structure/classify.rs +94 -0
  31. package/crates/naome-core/src/quality/structure/config.rs +89 -0
  32. package/crates/naome-core/src/quality/structure/defaults.rs +107 -0
  33. package/crates/naome-core/src/quality/structure/mod.rs +77 -0
  34. package/crates/naome-core/src/quality/structure/model.rs +124 -0
  35. package/crates/naome-core/src/quality/types.rs +3 -0
  36. package/crates/naome-core/src/route/builtin_checks.rs +155 -0
  37. package/crates/naome-core/src/route/builtin_context.rs +73 -0
  38. package/crates/naome-core/src/route/builtin_integrity.rs +49 -0
  39. package/crates/naome-core/src/route/builtin_require.rs +40 -0
  40. package/crates/naome-core/src/route/context.rs +180 -0
  41. package/crates/naome-core/src/route/execution.rs +96 -0
  42. package/crates/naome-core/src/route/execution_baselines.rs +146 -0
  43. package/crates/naome-core/src/route/execution_support.rs +57 -0
  44. package/crates/naome-core/src/route/execution_tasks.rs +71 -0
  45. package/crates/naome-core/src/route/git_ops.rs +72 -0
  46. package/crates/naome-core/src/route/quality_gate.rs +73 -0
  47. package/crates/naome-core/src/route/quality_gate_config.rs +126 -0
  48. package/crates/naome-core/src/route/quality_gate_snapshot.rs +69 -0
  49. package/crates/naome-core/src/route/worktree.rs +75 -0
  50. package/crates/naome-core/src/route/worktree_files.rs +32 -0
  51. package/crates/naome-core/src/route/worktree_plan.rs +131 -0
  52. package/crates/naome-core/src/route.rs +44 -1217
  53. package/crates/naome-core/src/verification.rs +1 -0
  54. package/crates/naome-core/tests/decision.rs +24 -118
  55. package/crates/naome-core/tests/harness_health.rs +2 -0
  56. package/crates/naome-core/tests/quality.rs +12 -118
  57. package/crates/naome-core/tests/quality_structure.rs +116 -0
  58. package/crates/naome-core/tests/quality_structure_adapters.rs +98 -0
  59. package/crates/naome-core/tests/quality_structure_policy.rs +125 -0
  60. package/crates/naome-core/tests/quality_structure_support/mod.rs +249 -0
  61. package/crates/naome-core/tests/repo_support/mod.rs +16 -0
  62. package/crates/naome-core/tests/repo_support/repo.rs +113 -0
  63. package/crates/naome-core/tests/repo_support/repo_factories.rs +99 -0
  64. package/crates/naome-core/tests/repo_support/repo_helpers.rs +123 -0
  65. package/crates/naome-core/tests/repo_support/routes.rs +81 -0
  66. package/crates/naome-core/tests/repo_support/verification.rs +168 -0
  67. package/crates/naome-core/tests/repo_support/verification_values.rs +135 -0
  68. package/crates/naome-core/tests/route.rs +1 -1376
  69. package/crates/naome-core/tests/route_baseline.rs +86 -0
  70. package/crates/naome-core/tests/route_completion.rs +141 -0
  71. package/crates/naome-core/tests/route_harness_refresh.rs +135 -0
  72. package/crates/naome-core/tests/route_user_diff.rs +198 -0
  73. package/crates/naome-core/tests/route_worktree.rs +54 -0
  74. package/crates/naome-core/tests/task_state.rs +60 -432
  75. package/crates/naome-core/tests/task_state_compact_support/repo.rs +1 -1
  76. package/crates/naome-core/tests/task_state_support/mod.rs +163 -0
  77. package/crates/naome-core/tests/task_state_support/states.rs +84 -0
  78. package/crates/naome-core/tests/verification.rs +4 -45
  79. package/crates/naome-core/tests/verification_contract.rs +22 -78
  80. package/crates/naome-core/tests/workflow_support/mod.rs +1 -1
  81. package/installer/agents.js +90 -0
  82. package/installer/context.js +67 -0
  83. package/installer/filesystem.js +166 -0
  84. package/installer/flows.js +84 -0
  85. package/installer/git-boundary.js +170 -0
  86. package/installer/git-hook-content.js +36 -0
  87. package/installer/git-hooks.js +134 -0
  88. package/installer/git-local.js +2 -0
  89. package/installer/git-shared.js +35 -0
  90. package/installer/harness-file-ops.js +140 -0
  91. package/installer/harness-files.js +56 -0
  92. package/installer/harness-verification.js +123 -0
  93. package/installer/install-plan.js +66 -0
  94. package/installer/main.js +25 -0
  95. package/installer/manifest-state.js +167 -0
  96. package/installer/native-build.js +24 -0
  97. package/installer/native-format.js +6 -0
  98. package/installer/native.js +162 -0
  99. package/installer/output.js +131 -0
  100. package/installer/version.js +32 -0
  101. package/native/darwin-arm64/naome +0 -0
  102. package/native/linux-x64/naome +0 -0
  103. package/package.json +2 -1
  104. package/templates/naome-root/.naome/bin/check-harness-health.js +2 -2
  105. package/templates/naome-root/.naome/bin/check-task-state.js +2 -2
  106. package/templates/naome-root/.naome/bin/naome.js +25 -21
  107. package/templates/naome-root/.naome/manifest.json +4 -2
  108. package/templates/naome-root/.naome/repository-structure.json +90 -0
  109. package/templates/naome-root/.naome/verification.json +1 -0
  110. package/templates/naome-root/docs/naome/index.md +4 -3
  111. package/templates/naome-root/docs/naome/repository-quality.md +3 -0
  112. package/templates/naome-root/docs/naome/repository-structure.md +51 -0
  113. package/templates/naome-root/docs/naome/testing.md +2 -1
@@ -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
+ }
@@ -0,0 +1,72 @@
1
+ use std::path::Path;
2
+ use std::process::{Command, Output};
3
+
4
+ use crate::models::NaomeError;
5
+ use crate::task_state::completed_task_commit_paths;
6
+
7
+ pub(super) fn git_head(root: &Path) -> Result<Option<String>, NaomeError> {
8
+ let output = git_output(root, &["rev-parse", "HEAD"])?;
9
+ if !output.status.success() {
10
+ return Ok(None);
11
+ }
12
+ Ok(Some(
13
+ String::from_utf8_lossy(&output.stdout).trim().to_string(),
14
+ ))
15
+ }
16
+
17
+ pub(super) fn git_add_all(root: &Path) -> Result<(), NaomeError> {
18
+ ensure_git_success(git_output(root, &["add", "-A"])?)
19
+ }
20
+
21
+ pub(super) fn git_add_completed_task_paths(root: &Path) -> Result<(), NaomeError> {
22
+ let paths = completed_task_commit_paths(root)?;
23
+ if paths.is_empty() {
24
+ return Err(NaomeError::new(
25
+ "No task-owned paths are available to commit.",
26
+ ));
27
+ }
28
+ git_stage_only_paths(root, &paths)
29
+ }
30
+
31
+ pub(super) fn git_stage_only_paths(root: &Path, paths: &[String]) -> Result<(), NaomeError> {
32
+ ensure_git_success(git_output(root, &["reset", "-q", "--", "."])?)?;
33
+ git_add_paths(root, paths)
34
+ }
35
+
36
+ fn git_add_paths(root: &Path, paths: &[String]) -> Result<(), NaomeError> {
37
+ if paths.is_empty() {
38
+ return Ok(());
39
+ }
40
+ let mut args = vec!["add", "--"];
41
+ args.extend(paths.iter().map(String::as_str));
42
+ let output = Command::new("git").args(args).current_dir(root).output()?;
43
+ if output.status.success() {
44
+ Ok(())
45
+ } else {
46
+ Err(NaomeError::new(command_output(&output)))
47
+ }
48
+ }
49
+
50
+ pub(super) fn git_commit(root: &Path, message: &str) -> Result<(), NaomeError> {
51
+ ensure_git_success(git_output(root, &["commit", "-m", message])?)
52
+ }
53
+
54
+ pub(super) fn git_output(root: &Path, args: &[&str]) -> Result<Output, NaomeError> {
55
+ Ok(Command::new("git").args(args).current_dir(root).output()?)
56
+ }
57
+
58
+ pub(super) fn ensure_git_success(output: Output) -> Result<(), NaomeError> {
59
+ if output.status.success() {
60
+ Ok(())
61
+ } else {
62
+ Err(NaomeError::new(command_output(&output)))
63
+ }
64
+ }
65
+
66
+ pub(super) fn command_output(output: &std::process::Output) -> String {
67
+ let stderr = String::from_utf8_lossy(&output.stderr).trim().to_string();
68
+ if !stderr.is_empty() {
69
+ return stderr;
70
+ }
71
+ String::from_utf8_lossy(&output.stdout).trim().to_string()
72
+ }