@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.
- package/Cargo.lock +2 -2
- package/Cargo.toml +1 -1
- package/LICENSE +180 -21
- package/README.md +49 -6
- package/bin/naome-node.js +2 -1579
- package/bin/naome.js +68 -16
- package/crates/naome-cli/Cargo.toml +1 -1
- package/crates/naome-cli/src/check_commands.rs +135 -0
- package/crates/naome-cli/src/cli_args.rs +5 -0
- package/crates/naome-cli/src/dispatcher.rs +37 -0
- package/crates/naome-cli/src/install_bridge.rs +83 -0
- package/crates/naome-cli/src/main.rs +60 -341
- package/crates/naome-cli/src/prompt_commands.rs +68 -0
- package/crates/naome-cli/src/quality_commands.rs +229 -0
- package/crates/naome-cli/src/simple_commands.rs +53 -0
- package/crates/naome-cli/src/workflow_commands.rs +153 -0
- package/crates/naome-core/Cargo.toml +1 -1
- package/crates/naome-core/src/decision/checks.rs +64 -0
- package/crates/naome-core/src/decision/idle.rs +67 -0
- package/crates/naome-core/src/decision/json.rs +36 -0
- package/crates/naome-core/src/decision/states.rs +165 -0
- package/crates/naome-core/src/decision.rs +131 -353
- package/crates/naome-core/src/harness_health/integrity.rs +96 -0
- package/crates/naome-core/src/harness_health.rs +14 -126
- package/crates/naome-core/src/install_plan.rs +5 -0
- package/crates/naome-core/src/intent/classifier.rs +171 -0
- package/crates/naome-core/src/intent/envelope.rs +108 -0
- package/crates/naome-core/src/intent/legacy.rs +138 -0
- package/crates/naome-core/src/intent/legacy_response.rs +76 -0
- package/crates/naome-core/src/intent/model.rs +71 -0
- package/crates/naome-core/src/intent/patterns.rs +170 -0
- package/crates/naome-core/src/intent/resolver.rs +162 -0
- package/crates/naome-core/src/intent/resolver_active.rs +17 -0
- package/crates/naome-core/src/intent/resolver_baseline.rs +55 -0
- package/crates/naome-core/src/intent/resolver_catalog.rs +167 -0
- package/crates/naome-core/src/intent/resolver_policy.rs +72 -0
- package/crates/naome-core/src/intent/resolver_shared.rs +55 -0
- package/crates/naome-core/src/intent/risk.rs +40 -0
- package/crates/naome-core/src/intent/segment.rs +170 -0
- package/crates/naome-core/src/intent.rs +64 -879
- package/crates/naome-core/src/journal.rs +9 -20
- package/crates/naome-core/src/lib.rs +15 -0
- package/crates/naome-core/src/paths.rs +3 -1
- package/crates/naome-core/src/quality/adapter_support.rs +89 -0
- package/crates/naome-core/src/quality/adapters.rs +131 -0
- package/crates/naome-core/src/quality/baseline.rs +75 -0
- package/crates/naome-core/src/quality/checks/duplicate_blocks.rs +175 -0
- package/crates/naome-core/src/quality/checks/near_duplicates.rs +130 -0
- package/crates/naome-core/src/quality/checks.rs +228 -0
- package/crates/naome-core/src/quality/cleanup.rs +84 -0
- package/crates/naome-core/src/quality/config.rs +102 -0
- package/crates/naome-core/src/quality/config_support.rs +24 -0
- package/crates/naome-core/src/quality/mod.rs +108 -0
- package/crates/naome-core/src/quality/scanner/repo_paths.rs +103 -0
- package/crates/naome-core/src/quality/scanner.rs +379 -0
- package/crates/naome-core/src/quality/structure/adapters.rs +84 -0
- package/crates/naome-core/src/quality/structure/checks/basic.rs +153 -0
- package/crates/naome-core/src/quality/structure/checks/directory.rs +144 -0
- package/crates/naome-core/src/quality/structure/checks/pairing.rs +63 -0
- package/crates/naome-core/src/quality/structure/checks.rs +124 -0
- package/crates/naome-core/src/quality/structure/classify/roles.rs +188 -0
- package/crates/naome-core/src/quality/structure/classify.rs +94 -0
- package/crates/naome-core/src/quality/structure/config.rs +89 -0
- package/crates/naome-core/src/quality/structure/defaults.rs +107 -0
- package/crates/naome-core/src/quality/structure/mod.rs +77 -0
- package/crates/naome-core/src/quality/structure/model.rs +124 -0
- package/crates/naome-core/src/quality/types.rs +292 -0
- package/crates/naome-core/src/route/builtin_checks.rs +155 -0
- package/crates/naome-core/src/route/builtin_context.rs +73 -0
- package/crates/naome-core/src/route/builtin_integrity.rs +49 -0
- package/crates/naome-core/src/route/builtin_require.rs +40 -0
- package/crates/naome-core/src/route/context.rs +180 -0
- package/crates/naome-core/src/route/execution.rs +96 -0
- package/crates/naome-core/src/route/execution_baselines.rs +146 -0
- package/crates/naome-core/src/route/execution_support.rs +57 -0
- package/crates/naome-core/src/route/execution_tasks.rs +71 -0
- package/crates/naome-core/src/route/git_ops.rs +72 -0
- package/crates/naome-core/src/route/quality_gate.rs +73 -0
- package/crates/naome-core/src/route/quality_gate_config.rs +126 -0
- package/crates/naome-core/src/route/quality_gate_snapshot.rs +69 -0
- package/crates/naome-core/src/route/worktree.rs +75 -0
- package/crates/naome-core/src/route/worktree_files.rs +32 -0
- package/crates/naome-core/src/route/worktree_plan.rs +131 -0
- package/crates/naome-core/src/route.rs +44 -1155
- package/crates/naome-core/src/task_state/admission.rs +63 -0
- package/crates/naome-core/src/task_state/admission_proof.rs +72 -0
- package/crates/naome-core/src/task_state/api.rs +130 -0
- package/crates/naome-core/src/task_state/commit_gate.rs +138 -0
- package/crates/naome-core/src/task_state/compact_proof.rs +160 -0
- package/crates/naome-core/src/task_state/completed_refresh.rs +89 -0
- package/crates/naome-core/src/task_state/completion.rs +72 -0
- package/crates/naome-core/src/task_state/deleted_paths.rs +47 -0
- package/crates/naome-core/src/task_state/diff.rs +95 -0
- package/crates/naome-core/src/task_state/evidence.rs +154 -0
- package/crates/naome-core/src/task_state/git_io.rs +86 -0
- package/crates/naome-core/src/task_state/git_parse.rs +86 -0
- package/crates/naome-core/src/task_state/git_refs.rs +37 -0
- package/crates/naome-core/src/task_state/human_review_state.rs +31 -0
- package/crates/naome-core/src/task_state/mod.rs +38 -0
- package/crates/naome-core/src/task_state/process_guard.rs +40 -0
- package/crates/naome-core/src/task_state/progress.rs +123 -0
- package/crates/naome-core/src/task_state/proof.rs +139 -0
- package/crates/naome-core/src/task_state/proof_entry.rs +66 -0
- package/crates/naome-core/src/task_state/proof_model.rs +70 -0
- package/crates/naome-core/src/task_state/proof_sources.rs +76 -0
- package/crates/naome-core/src/task_state/push_gate.rs +49 -0
- package/crates/naome-core/src/task_state/reconcile.rs +7 -0
- package/crates/naome-core/src/task_state/repair.rs +168 -0
- package/crates/naome-core/src/task_state/shape.rs +117 -0
- package/crates/naome-core/src/task_state/task_diff_api.rs +170 -0
- package/crates/naome-core/src/task_state/task_records.rs +131 -0
- package/crates/naome-core/src/task_state/task_references.rs +126 -0
- package/crates/naome-core/src/task_state/types.rs +87 -0
- package/crates/naome-core/src/task_state/util.rs +137 -0
- package/crates/naome-core/src/verification/render.rs +122 -0
- package/crates/naome-core/src/verification.rs +177 -58
- package/crates/naome-core/src/verification_contract.rs +49 -21
- package/crates/naome-core/src/workflow/integrity.rs +123 -0
- package/crates/naome-core/src/workflow/integrity_normalize.rs +7 -0
- package/crates/naome-core/src/workflow/integrity_support.rs +110 -0
- package/crates/naome-core/src/workflow/mod.rs +18 -0
- package/crates/naome-core/src/workflow/mutation.rs +68 -0
- package/crates/naome-core/src/workflow/output.rs +111 -0
- package/crates/naome-core/src/workflow/phase_inference.rs +73 -0
- package/crates/naome-core/src/workflow/phases.rs +169 -0
- package/crates/naome-core/src/workflow/policy.rs +156 -0
- package/crates/naome-core/src/workflow/processes.rs +91 -0
- package/crates/naome-core/src/workflow/types.rs +42 -0
- package/crates/naome-core/tests/decision.rs +24 -118
- package/crates/naome-core/tests/harness_health.rs +5 -0
- package/crates/naome-core/tests/intent.rs +97 -792
- package/crates/naome-core/tests/intent_support/mod.rs +133 -0
- package/crates/naome-core/tests/intent_v2.rs +90 -0
- package/crates/naome-core/tests/quality.rs +319 -0
- package/crates/naome-core/tests/quality_structure.rs +116 -0
- package/crates/naome-core/tests/quality_structure_adapters.rs +98 -0
- package/crates/naome-core/tests/quality_structure_policy.rs +125 -0
- package/crates/naome-core/tests/quality_structure_support/mod.rs +249 -0
- package/crates/naome-core/tests/repo_support/mod.rs +16 -0
- package/crates/naome-core/tests/repo_support/repo.rs +113 -0
- package/crates/naome-core/tests/repo_support/repo_factories.rs +99 -0
- package/crates/naome-core/tests/repo_support/repo_helpers.rs +123 -0
- package/crates/naome-core/tests/repo_support/routes.rs +81 -0
- package/crates/naome-core/tests/repo_support/verification.rs +168 -0
- package/crates/naome-core/tests/repo_support/verification_values.rs +135 -0
- package/crates/naome-core/tests/route.rs +1 -1476
- package/crates/naome-core/tests/route_baseline.rs +86 -0
- package/crates/naome-core/tests/route_completion.rs +141 -0
- package/crates/naome-core/tests/route_harness_refresh.rs +135 -0
- package/crates/naome-core/tests/route_user_diff.rs +198 -0
- package/crates/naome-core/tests/route_worktree.rs +54 -0
- package/crates/naome-core/tests/task_state.rs +60 -429
- package/crates/naome-core/tests/task_state_compact.rs +110 -0
- package/crates/naome-core/tests/task_state_compact_support/mod.rs +5 -0
- package/crates/naome-core/tests/task_state_compact_support/repo.rs +130 -0
- package/crates/naome-core/tests/task_state_compact_support/states.rs +151 -0
- package/crates/naome-core/tests/task_state_support/mod.rs +163 -0
- package/crates/naome-core/tests/task_state_support/states.rs +84 -0
- package/crates/naome-core/tests/verification.rs +4 -45
- package/crates/naome-core/tests/verification_contract.rs +22 -78
- package/crates/naome-core/tests/workflow_integrity.rs +85 -0
- package/crates/naome-core/tests/workflow_policy.rs +139 -0
- package/crates/naome-core/tests/workflow_support/mod.rs +194 -0
- package/installer/agents.js +90 -0
- package/installer/context.js +67 -0
- package/installer/filesystem.js +166 -0
- package/installer/flows.js +84 -0
- package/installer/git-boundary.js +170 -0
- package/installer/git-hook-content.js +36 -0
- package/installer/git-hooks.js +134 -0
- package/installer/git-local.js +2 -0
- package/installer/git-shared.js +35 -0
- package/installer/harness-file-ops.js +140 -0
- package/installer/harness-files.js +56 -0
- package/installer/harness-verification.js +123 -0
- package/installer/install-plan.js +66 -0
- package/installer/main.js +25 -0
- package/installer/manifest-state.js +167 -0
- package/installer/native-build.js +24 -0
- package/installer/native-format.js +6 -0
- package/installer/native.js +162 -0
- package/installer/output.js +131 -0
- package/installer/version.js +32 -0
- package/native/darwin-arm64/naome +0 -0
- package/native/linux-x64/naome +0 -0
- package/package.json +3 -2
- package/templates/naome-root/.naome/bin/check-harness-health.js +66 -85
- package/templates/naome-root/.naome/bin/check-task-state.js +9 -10
- package/templates/naome-root/.naome/bin/naome.js +51 -76
- package/templates/naome-root/.naome/manifest.json +22 -18
- package/templates/naome-root/.naome/repository-quality-baseline.json +5 -0
- package/templates/naome-root/.naome/repository-quality.json +24 -0
- package/templates/naome-root/.naome/repository-structure.json +90 -0
- package/templates/naome-root/.naome/task-contract.schema.json +93 -11
- package/templates/naome-root/.naome/upgrade-state.json +1 -1
- package/templates/naome-root/.naome/verification.json +38 -0
- package/templates/naome-root/AGENTS.md +3 -0
- package/templates/naome-root/docs/naome/agent-workflow.md +25 -12
- package/templates/naome-root/docs/naome/execution.md +25 -21
- package/templates/naome-root/docs/naome/index.md +5 -3
- package/templates/naome-root/docs/naome/repository-quality.md +46 -0
- package/templates/naome-root/docs/naome/repository-structure.md +51 -0
- package/templates/naome-root/docs/naome/testing.md +13 -0
- package/crates/naome-core/src/task_state.rs +0 -2210
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
use serde::Serialize;
|
|
2
|
+
|
|
3
|
+
use crate::models::Decision;
|
|
4
|
+
|
|
5
|
+
use super::classifier::{has_candidate, winning_intent};
|
|
6
|
+
use super::legacy_response::response_policy;
|
|
7
|
+
use super::model::{CanonicalIntent, IntentKind};
|
|
8
|
+
use super::resolver::ResolvedIntent;
|
|
9
|
+
|
|
10
|
+
#[derive(Debug, Clone, Serialize, PartialEq, Eq)]
|
|
11
|
+
#[serde(rename_all = "camelCase")]
|
|
12
|
+
pub struct IntentDecision {
|
|
13
|
+
pub schema: String,
|
|
14
|
+
pub repo_state: String,
|
|
15
|
+
pub blocked: bool,
|
|
16
|
+
pub prompt_intent: String,
|
|
17
|
+
pub certainty: String,
|
|
18
|
+
pub policy_action: String,
|
|
19
|
+
pub allowed: bool,
|
|
20
|
+
pub summary: String,
|
|
21
|
+
pub next_action: String,
|
|
22
|
+
pub user_message: String,
|
|
23
|
+
pub human_options: Vec<String>,
|
|
24
|
+
pub internal_notes: Vec<String>,
|
|
25
|
+
pub reason_codes: Vec<String>,
|
|
26
|
+
pub risk_codes: Vec<String>,
|
|
27
|
+
pub changed_paths: Vec<String>,
|
|
28
|
+
pub required_context: Vec<String>,
|
|
29
|
+
pub evidence: PromptEvidence,
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
#[derive(Debug, Clone, Serialize, PartialEq, Eq)]
|
|
33
|
+
#[serde(rename_all = "camelCase")]
|
|
34
|
+
pub struct PromptEvidence {
|
|
35
|
+
pub references_current_task: bool,
|
|
36
|
+
pub requests_new_goal: bool,
|
|
37
|
+
pub requests_correction: bool,
|
|
38
|
+
pub requests_status: bool,
|
|
39
|
+
pub requests_review: bool,
|
|
40
|
+
pub requests_repair: bool,
|
|
41
|
+
pub requests_commit: bool,
|
|
42
|
+
pub requests_no_commit: bool,
|
|
43
|
+
pub requests_cancel: bool,
|
|
44
|
+
pub requests_completion: bool,
|
|
45
|
+
pub has_risky_terms: bool,
|
|
46
|
+
pub is_empty: bool,
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
pub(crate) fn legacy_decision(
|
|
50
|
+
decision: Decision,
|
|
51
|
+
canonical: CanonicalIntent,
|
|
52
|
+
resolved: ResolvedIntent,
|
|
53
|
+
) -> IntentDecision {
|
|
54
|
+
let evidence = PromptEvidence::from_canonical(&canonical);
|
|
55
|
+
let (user_message, human_options, mut internal_notes) = response_policy(
|
|
56
|
+
&decision,
|
|
57
|
+
resolved.prompt_intent.as_str(),
|
|
58
|
+
&resolved.policy_action,
|
|
59
|
+
&resolved.summary,
|
|
60
|
+
);
|
|
61
|
+
for code in &resolved.reason_codes {
|
|
62
|
+
if !internal_notes.contains(code) {
|
|
63
|
+
internal_notes.push(code.clone());
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
IntentDecision {
|
|
68
|
+
schema: "naome.intent.v1".to_string(),
|
|
69
|
+
repo_state: decision.state,
|
|
70
|
+
blocked: decision.blocked,
|
|
71
|
+
prompt_intent: resolved.prompt_intent.as_str().to_string(),
|
|
72
|
+
certainty: resolved.certainty,
|
|
73
|
+
policy_action: resolved.policy_action,
|
|
74
|
+
allowed: resolved.allowed,
|
|
75
|
+
summary: resolved.summary,
|
|
76
|
+
next_action: resolved.next_action,
|
|
77
|
+
user_message,
|
|
78
|
+
human_options,
|
|
79
|
+
internal_notes,
|
|
80
|
+
reason_codes: resolved.reason_codes,
|
|
81
|
+
risk_codes: resolved.risk_codes,
|
|
82
|
+
changed_paths: decision.changed_paths,
|
|
83
|
+
required_context: decision.required_context,
|
|
84
|
+
evidence,
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
pub fn format_intent(intent: &IntentDecision) -> String {
|
|
89
|
+
let mut lines = vec![
|
|
90
|
+
format!("NAOME intent: {}", intent.prompt_intent),
|
|
91
|
+
format!("Repo state: {}", intent.repo_state),
|
|
92
|
+
format!("Allowed: {}", intent.allowed),
|
|
93
|
+
format!("Policy action: {}", intent.policy_action),
|
|
94
|
+
format!("Summary: {}", intent.user_message),
|
|
95
|
+
format!("Next action: {}", intent.next_action),
|
|
96
|
+
];
|
|
97
|
+
|
|
98
|
+
if !intent.human_options.is_empty() {
|
|
99
|
+
lines.push(format!(
|
|
100
|
+
"Human options: {}",
|
|
101
|
+
intent.human_options.join(", ")
|
|
102
|
+
));
|
|
103
|
+
}
|
|
104
|
+
if !intent.reason_codes.is_empty() {
|
|
105
|
+
lines.push(format!("Reason codes: {}", intent.reason_codes.join(", ")));
|
|
106
|
+
}
|
|
107
|
+
if !intent.risk_codes.is_empty() {
|
|
108
|
+
lines.push(format!("Risk codes: {}", intent.risk_codes.join(", ")));
|
|
109
|
+
}
|
|
110
|
+
if !intent.changed_paths.is_empty() {
|
|
111
|
+
lines.push(format!(
|
|
112
|
+
"Changed paths: {}",
|
|
113
|
+
intent.changed_paths.join(", ")
|
|
114
|
+
));
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
lines.push(String::new());
|
|
118
|
+
lines.join("\n")
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
impl PromptEvidence {
|
|
122
|
+
fn from_canonical(canonical: &CanonicalIntent) -> Self {
|
|
123
|
+
Self {
|
|
124
|
+
references_current_task: canonical.references_current_task,
|
|
125
|
+
requests_new_goal: has_candidate(canonical, IntentKind::NewTask),
|
|
126
|
+
requests_correction: has_candidate(canonical, IntentKind::TaskRevision),
|
|
127
|
+
requests_status: winning_intent(canonical) == IntentKind::StatusQuestion,
|
|
128
|
+
requests_review: has_candidate(canonical, IntentKind::ReviewRequest),
|
|
129
|
+
requests_repair: has_candidate(canonical, IntentKind::RepairRequest),
|
|
130
|
+
requests_commit: has_candidate(canonical, IntentKind::CommitRequest),
|
|
131
|
+
requests_no_commit: has_candidate(canonical, IntentKind::NoCommitRequest),
|
|
132
|
+
requests_cancel: has_candidate(canonical, IntentKind::CancelRequest),
|
|
133
|
+
requests_completion: has_candidate(canonical, IntentKind::TaskCompletion),
|
|
134
|
+
has_risky_terms: canonical.risk.has_risky_terms,
|
|
135
|
+
is_empty: canonical.is_empty,
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
use crate::models::Decision;
|
|
2
|
+
|
|
3
|
+
pub(crate) fn response_policy(
|
|
4
|
+
decision: &Decision,
|
|
5
|
+
prompt_intent: &str,
|
|
6
|
+
policy_action: &str,
|
|
7
|
+
summary: &str,
|
|
8
|
+
) -> (String, Vec<String>, Vec<String>) {
|
|
9
|
+
let internal_notes = response_notes(decision, prompt_intent, policy_action);
|
|
10
|
+
let human_options = response_human_options(decision, policy_action);
|
|
11
|
+
let user_message = match policy_action {
|
|
12
|
+
"auto_commit_completed_task_then_create_new_task" => "The completed task is valid. NAOME can baseline it automatically before starting the next separate task.",
|
|
13
|
+
"auto_commit_completed_task_then_create_isolated_task_worktree" => "The completed task is valid. NAOME can baseline only that task, preserve unrelated user edits, and create an isolated worktree for the next task.",
|
|
14
|
+
"auto_commit_harness_refresh_then_create_new_task" => "NAOME can baseline deterministic harness refresh changes first, then create the next task in the same worktree.",
|
|
15
|
+
"auto_commit_harness_refresh_then_create_isolated_task_worktree" => "NAOME can baseline deterministic harness refresh changes first, then create an isolated task worktree and leave user edits untouched.",
|
|
16
|
+
"auto_commit_harness_refresh_baseline" => "NAOME can baseline deterministic harness refresh changes and leave unrelated user edits untouched.",
|
|
17
|
+
"create_isolated_task_worktree" => "NAOME can create an isolated task worktree and leave existing user edits untouched.",
|
|
18
|
+
"commit_user_diff_with_quality_gate" => "NAOME can commit the current user-owned diff only after the configured quality gate passes. It will stage only the changed paths present before the gate runs.",
|
|
19
|
+
"auto_commit_harness_refresh_then_completed_task_then_create_new_task" => "The completed task is valid after separating deterministic harness refresh changes. NAOME can baseline the refresh first, then baseline the task before starting the next separate task.",
|
|
20
|
+
"auto_commit_upgrade_baseline_then_create_new_task" => "The setup or repair diff is ready. NAOME can baseline it automatically before starting the next task.",
|
|
21
|
+
"create_new_task" | "create_new_task_without_auto_baseline" => "NAOME is ready to create the next task.",
|
|
22
|
+
"answer_status_only" => summary,
|
|
23
|
+
"block_auto_baseline_due_to_no_commit" => "I will not baseline or commit because the prompt explicitly blocks committing. Review, revise, cancel, or clear the current diff first.",
|
|
24
|
+
"review_task_diff" | "review_diff_first" | "review_current_task_diff" => "The prompt asks for review, so NAOME will not mutate the repository automatically.",
|
|
25
|
+
"cancel_task_changes" | "cancel_upgrade_baseline" | "cancel_current_task" => "The prompt asks to cancel changes, so NAOME will not start new work until that decision is handled.",
|
|
26
|
+
"block_unsafe_intent" => "NAOME blocked this request because it contains unsafe or guardrail-bypass wording.",
|
|
27
|
+
"block_unowned_diff" => "NAOME will not commit or clear unowned changes automatically. Review the existing diff, or ask for a separate new task so NAOME can use an isolated worktree and leave those edits untouched.",
|
|
28
|
+
"repair_harness_only" => "NAOME harness health failed, so normal task work is blocked until repair or review.",
|
|
29
|
+
_ => summary,
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
(user_message.to_string(), human_options, internal_notes)
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
fn response_notes(decision: &Decision, prompt_intent: &str, policy_action: &str) -> Vec<String> {
|
|
36
|
+
let mut notes = vec![
|
|
37
|
+
format!("repo_state:{}", decision.state),
|
|
38
|
+
format!("prompt_intent:{prompt_intent}"),
|
|
39
|
+
format!("policy_action:{policy_action}"),
|
|
40
|
+
];
|
|
41
|
+
if !decision.allowed_actions.is_empty() {
|
|
42
|
+
notes.push(format!(
|
|
43
|
+
"internal_allowed_actions:{}",
|
|
44
|
+
decision.allowed_actions.join(",")
|
|
45
|
+
));
|
|
46
|
+
}
|
|
47
|
+
notes
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
fn response_human_options(decision: &Decision, policy_action: &str) -> Vec<String> {
|
|
51
|
+
match policy_action {
|
|
52
|
+
"review_task_diff" => decision.allowed_actions.clone(),
|
|
53
|
+
"cancel_task_changes" => vec![
|
|
54
|
+
"review_task_diff".to_string(),
|
|
55
|
+
"cancel_task_changes".to_string(),
|
|
56
|
+
],
|
|
57
|
+
"block_auto_baseline_due_to_no_commit" => decision
|
|
58
|
+
.allowed_actions
|
|
59
|
+
.iter()
|
|
60
|
+
.filter(|action| !action.starts_with("commit_"))
|
|
61
|
+
.cloned()
|
|
62
|
+
.collect(),
|
|
63
|
+
"block_unsafe_intent" => vec!["narrow_request".to_string(), "review_status".to_string()],
|
|
64
|
+
"block_ambiguous_intent" => decision.allowed_actions.clone(),
|
|
65
|
+
"block_unowned_diff" => decision
|
|
66
|
+
.allowed_actions
|
|
67
|
+
.iter()
|
|
68
|
+
.filter(|action| action.as_str() == "review_unowned_diff")
|
|
69
|
+
.cloned()
|
|
70
|
+
.collect(),
|
|
71
|
+
"repair_harness_only" => decision.allowed_actions.clone(),
|
|
72
|
+
"review_diff_first" | "cancel_upgrade_baseline" => decision.allowed_actions.clone(),
|
|
73
|
+
"review_current_task_diff" | "cancel_current_task" => decision.allowed_actions.clone(),
|
|
74
|
+
_ => Vec::new(),
|
|
75
|
+
}
|
|
76
|
+
}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
|
2
|
+
pub(crate) enum SegmentKind {
|
|
3
|
+
Prose,
|
|
4
|
+
InlineCode,
|
|
5
|
+
CodeFence,
|
|
6
|
+
QuotedText,
|
|
7
|
+
ListItem,
|
|
8
|
+
FilePath,
|
|
9
|
+
CommandLike,
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
#[derive(Debug, Clone, PartialEq, Eq)]
|
|
13
|
+
pub(crate) struct PromptSegment {
|
|
14
|
+
pub kind: SegmentKind,
|
|
15
|
+
pub text: String,
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
|
19
|
+
pub(crate) enum IntentKind {
|
|
20
|
+
Ambiguous,
|
|
21
|
+
NewTask,
|
|
22
|
+
TaskRevision,
|
|
23
|
+
StatusQuestion,
|
|
24
|
+
ReviewRequest,
|
|
25
|
+
RepairRequest,
|
|
26
|
+
CommitRequest,
|
|
27
|
+
NoCommitRequest,
|
|
28
|
+
CancelRequest,
|
|
29
|
+
TaskCompletion,
|
|
30
|
+
Unsafe,
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
impl IntentKind {
|
|
34
|
+
pub fn as_str(self) -> &'static str {
|
|
35
|
+
match self {
|
|
36
|
+
Self::Ambiguous => "ambiguous",
|
|
37
|
+
Self::NewTask => "new_task",
|
|
38
|
+
Self::TaskRevision => "task_revision",
|
|
39
|
+
Self::StatusQuestion => "status_question",
|
|
40
|
+
Self::ReviewRequest => "review_request",
|
|
41
|
+
Self::RepairRequest => "repair_request",
|
|
42
|
+
Self::CommitRequest => "commit_request",
|
|
43
|
+
Self::NoCommitRequest => "no_commit_request",
|
|
44
|
+
Self::CancelRequest => "cancel_request",
|
|
45
|
+
Self::TaskCompletion => "task_completion",
|
|
46
|
+
Self::Unsafe => "unsafe",
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
#[derive(Debug, Clone, PartialEq, Eq)]
|
|
52
|
+
pub(crate) struct IntentCandidate {
|
|
53
|
+
pub kind: IntentKind,
|
|
54
|
+
pub confidence: u8,
|
|
55
|
+
pub evidence: String,
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
#[derive(Debug, Clone, PartialEq, Eq)]
|
|
59
|
+
pub(crate) struct RiskContext {
|
|
60
|
+
pub has_risky_terms: bool,
|
|
61
|
+
pub risk_codes: Vec<String>,
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
#[derive(Debug, Clone, PartialEq, Eq)]
|
|
65
|
+
pub(crate) struct CanonicalIntent {
|
|
66
|
+
pub is_empty: bool,
|
|
67
|
+
pub references_current_task: bool,
|
|
68
|
+
pub candidates: Vec<IntentCandidate>,
|
|
69
|
+
pub risk: RiskContext,
|
|
70
|
+
pub has_workflow_conflict: bool,
|
|
71
|
+
}
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
use super::model::IntentKind;
|
|
2
|
+
|
|
3
|
+
pub(crate) fn normalized(text: &str) -> String {
|
|
4
|
+
text.to_lowercase()
|
|
5
|
+
.chars()
|
|
6
|
+
.map(|ch| if ch.is_alphanumeric() { ch } else { ' ' })
|
|
7
|
+
.collect::<String>()
|
|
8
|
+
.split_whitespace()
|
|
9
|
+
.collect::<Vec<_>>()
|
|
10
|
+
.join(" ")
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
pub(crate) fn lexical_tokens(text: &str) -> Vec<String> {
|
|
14
|
+
text.split_whitespace()
|
|
15
|
+
.map(|token| {
|
|
16
|
+
token
|
|
17
|
+
.trim_matches(|ch: char| !ch.is_alphanumeric() && ch != '_' && ch != '-')
|
|
18
|
+
.to_lowercase()
|
|
19
|
+
})
|
|
20
|
+
.filter(|token| !token.is_empty())
|
|
21
|
+
.collect()
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
pub(crate) fn structured_workflow_intents(text: &str) -> Vec<IntentKind> {
|
|
25
|
+
let tokens = lexical_tokens(text);
|
|
26
|
+
let mut intents = Vec::new();
|
|
27
|
+
|
|
28
|
+
if let Some(intent) = structured_action(&tokens) {
|
|
29
|
+
intents.push(intent);
|
|
30
|
+
}
|
|
31
|
+
if let Some(intent) = legacy_v1_short_action_request(&tokens) {
|
|
32
|
+
intents.push(intent);
|
|
33
|
+
}
|
|
34
|
+
if legacy_v1_no_commit(&tokens) {
|
|
35
|
+
intents.push(IntentKind::NoCommitRequest);
|
|
36
|
+
}
|
|
37
|
+
if has_structured_token(&tokens, &["no_commit", "without_commit"]) {
|
|
38
|
+
intents.push(IntentKind::NoCommitRequest);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
intents.sort_by_key(|kind| kind.as_str());
|
|
42
|
+
intents.dedup();
|
|
43
|
+
intents
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
pub(crate) fn command_workflow_intents(text: &str) -> Vec<IntentKind> {
|
|
47
|
+
let tokens = lexical_tokens(text);
|
|
48
|
+
let mut intents = Vec::new();
|
|
49
|
+
if let Some(intent) = cli_action(&tokens) {
|
|
50
|
+
intents.push(intent);
|
|
51
|
+
}
|
|
52
|
+
intents
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
pub(crate) fn explicit_task_intent(text: &str) -> Option<IntentKind> {
|
|
56
|
+
let tokens = lexical_tokens(text);
|
|
57
|
+
if has_structured_token(&tokens, &["task_revision", "current_task", "active_task"]) {
|
|
58
|
+
return Some(IntentKind::TaskRevision);
|
|
59
|
+
}
|
|
60
|
+
if has_structured_token(&tokens, &["new_task", "create_new_task", "separate_task"]) {
|
|
61
|
+
return Some(IntentKind::NewTask);
|
|
62
|
+
}
|
|
63
|
+
None
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
pub(crate) fn is_generic_work_request(text: &str) -> bool {
|
|
67
|
+
!lexical_tokens(text).is_empty()
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
fn structured_action(tokens: &[String]) -> Option<IntentKind> {
|
|
71
|
+
policy_action(tokens)
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
fn policy_action(tokens: &[String]) -> Option<IntentKind> {
|
|
75
|
+
for token in tokens {
|
|
76
|
+
let intent = match token.as_str() {
|
|
77
|
+
"commit_task_baseline" | "commit_user_diff" | "commit_upgrade_baseline" => {
|
|
78
|
+
IntentKind::CommitRequest
|
|
79
|
+
}
|
|
80
|
+
"review_task_diff" | "review_current_task_diff" | "review_unowned_diff" => {
|
|
81
|
+
IntentKind::ReviewRequest
|
|
82
|
+
}
|
|
83
|
+
"repair_harness" | "repair_harness_only" => IntentKind::RepairRequest,
|
|
84
|
+
"cancel_task_changes" | "cancel_current_task" => IntentKind::CancelRequest,
|
|
85
|
+
"continue_current_task" | "task_complete" | "complete_task" => {
|
|
86
|
+
IntentKind::TaskCompletion
|
|
87
|
+
}
|
|
88
|
+
"answer_status_only" => IntentKind::StatusQuestion,
|
|
89
|
+
"clear_or_commit_unowned_diff" => IntentKind::Ambiguous,
|
|
90
|
+
_ => continue,
|
|
91
|
+
};
|
|
92
|
+
return Some(intent);
|
|
93
|
+
}
|
|
94
|
+
None
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
fn cli_action(tokens: &[String]) -> Option<IntentKind> {
|
|
98
|
+
match (
|
|
99
|
+
tokens.first().map(String::as_str),
|
|
100
|
+
tokens.get(1).map(String::as_str),
|
|
101
|
+
) {
|
|
102
|
+
(Some("naome"), Some("commit")) => Some(IntentKind::CommitRequest),
|
|
103
|
+
(Some("naome"), Some("status")) => Some(IntentKind::StatusQuestion),
|
|
104
|
+
(Some("naome"), Some("sync" | "update" | "install")) => Some(IntentKind::RepairRequest),
|
|
105
|
+
(Some("git"), Some("commit")) => Some(IntentKind::CommitRequest),
|
|
106
|
+
_ => None,
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// Kept only so pre-v2 route callers keep their existing conservative behavior.
|
|
111
|
+
// A naome-intent-v2 envelope clears these candidates before policy resolution.
|
|
112
|
+
fn legacy_v1_short_action_request(tokens: &[String]) -> Option<IntentKind> {
|
|
113
|
+
let action_index = tokens
|
|
114
|
+
.iter()
|
|
115
|
+
.position(|token| legacy_v1_action_intent(token).is_some())?;
|
|
116
|
+
if action_index > 1 || tokens.len() > 6 {
|
|
117
|
+
return None;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
let intent = legacy_v1_action_intent(&tokens[action_index])?;
|
|
121
|
+
if intent == IntentKind::StatusQuestion || tokens.len() <= 3 {
|
|
122
|
+
return Some(intent);
|
|
123
|
+
}
|
|
124
|
+
if tokens.len() <= 2 || legacy_v1_has_action_scope(tokens) {
|
|
125
|
+
return Some(intent);
|
|
126
|
+
}
|
|
127
|
+
None
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
fn legacy_v1_no_commit(tokens: &[String]) -> bool {
|
|
131
|
+
tokens
|
|
132
|
+
.windows(3)
|
|
133
|
+
.any(|window| window[0] == "do" && window[1] == "not" && window[2] == "commit")
|
|
134
|
+
|| tokens
|
|
135
|
+
.windows(2)
|
|
136
|
+
.any(|window| matches!(window[0].as_str(), "no" | "without") && window[1] == "commit")
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
fn legacy_v1_action_intent(token: &str) -> Option<IntentKind> {
|
|
140
|
+
match token {
|
|
141
|
+
"commit" | "baseline" => Some(IntentKind::CommitRequest),
|
|
142
|
+
"review" => Some(IntentKind::ReviewRequest),
|
|
143
|
+
"repair" | "sync" => Some(IntentKind::RepairRequest),
|
|
144
|
+
"cancel" => Some(IntentKind::CancelRequest),
|
|
145
|
+
"complete" | "completion" => Some(IntentKind::TaskCompletion),
|
|
146
|
+
"status" => Some(IntentKind::StatusQuestion),
|
|
147
|
+
_ => None,
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
fn legacy_v1_has_action_scope(tokens: &[String]) -> bool {
|
|
152
|
+
has_structured_token(
|
|
153
|
+
tokens,
|
|
154
|
+
&[
|
|
155
|
+
"task",
|
|
156
|
+
"diff",
|
|
157
|
+
"harness",
|
|
158
|
+
"baseline",
|
|
159
|
+
"state",
|
|
160
|
+
"current_task",
|
|
161
|
+
"active_task",
|
|
162
|
+
],
|
|
163
|
+
)
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
fn has_structured_token(tokens: &[String], accepted: &[&str]) -> bool {
|
|
167
|
+
tokens
|
|
168
|
+
.iter()
|
|
169
|
+
.any(|token| accepted.iter().any(|accepted| token == accepted))
|
|
170
|
+
}
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
use crate::models::Decision;
|
|
2
|
+
|
|
3
|
+
use super::classifier::winning_intent;
|
|
4
|
+
use super::model::{CanonicalIntent, IntentKind};
|
|
5
|
+
use super::resolver_policy::state_policy;
|
|
6
|
+
use super::resolver_shared::{ambiguous_conflict, ambiguous_empty, unsafe_policy, Policy};
|
|
7
|
+
|
|
8
|
+
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
|
9
|
+
pub(crate) enum CompletedTaskReadiness {
|
|
10
|
+
Valid,
|
|
11
|
+
ValidAfterHarnessRefresh,
|
|
12
|
+
ValidWithUnrelatedDirty,
|
|
13
|
+
Invalid,
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
|
17
|
+
pub(crate) enum DirtyDiffReadiness {
|
|
18
|
+
Unclassified,
|
|
19
|
+
HarnessRefreshOnly,
|
|
20
|
+
HarnessRefreshWithUnrelatedDirty,
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
#[derive(Debug, Clone, PartialEq, Eq)]
|
|
24
|
+
pub(crate) struct ResolvedIntent {
|
|
25
|
+
pub prompt_intent: IntentKind,
|
|
26
|
+
pub certainty: String,
|
|
27
|
+
pub policy_action: String,
|
|
28
|
+
pub allowed: bool,
|
|
29
|
+
pub summary: String,
|
|
30
|
+
pub next_action: String,
|
|
31
|
+
pub reason_codes: Vec<String>,
|
|
32
|
+
pub risk_codes: Vec<String>,
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
pub(crate) fn resolve_intent(
|
|
36
|
+
decision: &Decision,
|
|
37
|
+
canonical: &CanonicalIntent,
|
|
38
|
+
completed_task_readiness: CompletedTaskReadiness,
|
|
39
|
+
dirty_diff_readiness: DirtyDiffReadiness,
|
|
40
|
+
) -> ResolvedIntent {
|
|
41
|
+
let prompt_intent = winning_intent(canonical);
|
|
42
|
+
let mut reason_codes = reason_codes(decision, canonical, prompt_intent);
|
|
43
|
+
let mut risk_codes = canonical.risk.risk_codes.clone();
|
|
44
|
+
if canonical.risk.has_risky_terms
|
|
45
|
+
&& !risk_codes
|
|
46
|
+
.iter()
|
|
47
|
+
.any(|code| code == "prompt_contains_risky_terms")
|
|
48
|
+
{
|
|
49
|
+
risk_codes.push("prompt_contains_risky_terms".to_string());
|
|
50
|
+
}
|
|
51
|
+
if completed_task_readiness == CompletedTaskReadiness::Invalid {
|
|
52
|
+
risk_codes.push("completed_task_proof_invalid".to_string());
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
let policy = resolve_policy(
|
|
56
|
+
decision,
|
|
57
|
+
canonical,
|
|
58
|
+
prompt_intent,
|
|
59
|
+
completed_task_readiness,
|
|
60
|
+
dirty_diff_readiness,
|
|
61
|
+
&mut reason_codes,
|
|
62
|
+
);
|
|
63
|
+
|
|
64
|
+
ResolvedIntent {
|
|
65
|
+
prompt_intent,
|
|
66
|
+
certainty: certainty(prompt_intent, &policy.0, &risk_codes),
|
|
67
|
+
policy_action: policy.0.to_string(),
|
|
68
|
+
allowed: policy.1,
|
|
69
|
+
summary: policy.2.to_string(),
|
|
70
|
+
next_action: policy.3.to_string(),
|
|
71
|
+
reason_codes,
|
|
72
|
+
risk_codes,
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
fn resolve_policy(
|
|
77
|
+
decision: &Decision,
|
|
78
|
+
canonical: &CanonicalIntent,
|
|
79
|
+
prompt_intent: IntentKind,
|
|
80
|
+
completed_task_readiness: CompletedTaskReadiness,
|
|
81
|
+
dirty_diff_readiness: DirtyDiffReadiness,
|
|
82
|
+
reason_codes: &mut Vec<String>,
|
|
83
|
+
) -> Policy {
|
|
84
|
+
if canonical.is_empty {
|
|
85
|
+
ambiguous_empty()
|
|
86
|
+
} else if canonical.has_workflow_conflict {
|
|
87
|
+
reason_codes.push("winning_rule:workflow_intent_conflict".to_string());
|
|
88
|
+
ambiguous_conflict()
|
|
89
|
+
} else if prompt_intent == IntentKind::Unsafe {
|
|
90
|
+
reason_codes.push("winning_rule:unsafe_intent_precedence".to_string());
|
|
91
|
+
unsafe_policy()
|
|
92
|
+
} else if prompt_intent == IntentKind::StatusQuestion {
|
|
93
|
+
(
|
|
94
|
+
"answer_status_only",
|
|
95
|
+
true,
|
|
96
|
+
"The prompt asks for status or explanation, so no task mutation is needed.",
|
|
97
|
+
"Answer from the current NAOME status without editing files.",
|
|
98
|
+
)
|
|
99
|
+
} else {
|
|
100
|
+
state_policy(
|
|
101
|
+
decision,
|
|
102
|
+
prompt_intent,
|
|
103
|
+
completed_task_readiness,
|
|
104
|
+
dirty_diff_readiness,
|
|
105
|
+
canonical.risk.has_risky_terms,
|
|
106
|
+
)
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
fn reason_codes(
|
|
111
|
+
decision: &Decision,
|
|
112
|
+
canonical: &CanonicalIntent,
|
|
113
|
+
prompt_intent: IntentKind,
|
|
114
|
+
) -> Vec<String> {
|
|
115
|
+
let mut codes = vec![format!("repo_state:{}", decision.state)];
|
|
116
|
+
if canonical.references_current_task {
|
|
117
|
+
codes.push("prompt_references_current_task".to_string());
|
|
118
|
+
}
|
|
119
|
+
for (kind, code) in [
|
|
120
|
+
(IntentKind::NewTask, "prompt_requests_new_goal"),
|
|
121
|
+
(IntentKind::TaskRevision, "prompt_requests_correction"),
|
|
122
|
+
(IntentKind::CommitRequest, "prompt_requests_commit"),
|
|
123
|
+
(IntentKind::NoCommitRequest, "prompt_blocks_commit"),
|
|
124
|
+
(IntentKind::ReviewRequest, "prompt_requests_review"),
|
|
125
|
+
(IntentKind::RepairRequest, "prompt_requests_repair"),
|
|
126
|
+
(IntentKind::CancelRequest, "prompt_requests_cancel"),
|
|
127
|
+
] {
|
|
128
|
+
if prompt_intent == kind
|
|
129
|
+
|| canonical
|
|
130
|
+
.candidates
|
|
131
|
+
.iter()
|
|
132
|
+
.any(|candidate| candidate.kind == kind)
|
|
133
|
+
{
|
|
134
|
+
codes.push(code.to_string());
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
codes
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
fn certainty(intent: IntentKind, policy_action: &str, risk_codes: &[String]) -> String {
|
|
141
|
+
if !risk_codes.is_empty() && policy_action == "block_unsafe_intent" {
|
|
142
|
+
"unsafe"
|
|
143
|
+
} else if policy_action == "block_ambiguous_intent"
|
|
144
|
+
|| policy_action == "block_auto_baseline_due_to_no_commit"
|
|
145
|
+
|| intent == IntentKind::Ambiguous
|
|
146
|
+
{
|
|
147
|
+
"ambiguous"
|
|
148
|
+
} else if matches!(
|
|
149
|
+
intent,
|
|
150
|
+
IntentKind::CommitRequest
|
|
151
|
+
| IntentKind::StatusQuestion
|
|
152
|
+
| IntentKind::ReviewRequest
|
|
153
|
+
| IntentKind::RepairRequest
|
|
154
|
+
| IntentKind::NoCommitRequest
|
|
155
|
+
| IntentKind::CancelRequest
|
|
156
|
+
) {
|
|
157
|
+
"exact"
|
|
158
|
+
} else {
|
|
159
|
+
"likely"
|
|
160
|
+
}
|
|
161
|
+
.to_string()
|
|
162
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
use super::model::IntentKind;
|
|
2
|
+
use super::resolver_catalog::{
|
|
3
|
+
POLICY_ACTIVE_CANCEL, POLICY_ACTIVE_CONTINUE, POLICY_ACTIVE_NEW_BLOCK, POLICY_ACTIVE_NO_COMMIT,
|
|
4
|
+
POLICY_ACTIVE_REVIEW,
|
|
5
|
+
};
|
|
6
|
+
use super::resolver_shared::{ambiguous_policy, Policy};
|
|
7
|
+
|
|
8
|
+
pub(crate) fn active_policy(intent: IntentKind) -> Policy {
|
|
9
|
+
match intent {
|
|
10
|
+
IntentKind::ReviewRequest => POLICY_ACTIVE_REVIEW,
|
|
11
|
+
IntentKind::CancelRequest => POLICY_ACTIVE_CANCEL,
|
|
12
|
+
IntentKind::NoCommitRequest => POLICY_ACTIVE_NO_COMMIT,
|
|
13
|
+
IntentKind::TaskRevision | IntentKind::TaskCompletion => POLICY_ACTIVE_CONTINUE,
|
|
14
|
+
IntentKind::NewTask => POLICY_ACTIVE_NEW_BLOCK,
|
|
15
|
+
_ => ambiguous_policy(),
|
|
16
|
+
}
|
|
17
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
use super::model::IntentKind;
|
|
2
|
+
use super::resolver::DirtyDiffReadiness;
|
|
3
|
+
use super::resolver_catalog::{
|
|
4
|
+
POLICY_BASELINE_BLOCK, POLICY_BASELINE_CANCEL, POLICY_BASELINE_COMMIT_UPGRADE,
|
|
5
|
+
POLICY_BASELINE_NEW_REFRESH, POLICY_BASELINE_NEW_UPGRADE, POLICY_BASELINE_NO_COMMIT,
|
|
6
|
+
POLICY_BASELINE_REVIEW, POLICY_DIRTY_BLOCK, POLICY_DIRTY_COMMIT_USER,
|
|
7
|
+
POLICY_DIRTY_NEW_ISOLATED, POLICY_DIRTY_NEW_REFRESH, POLICY_DIRTY_NEW_WORKTREE,
|
|
8
|
+
};
|
|
9
|
+
use super::resolver_shared::{harness_refresh_policy, unsafe_policy, Policy};
|
|
10
|
+
|
|
11
|
+
pub(crate) fn dirty_policy(intent: IntentKind, dirty: DirtyDiffReadiness, risky: bool) -> Policy {
|
|
12
|
+
match (intent, dirty, risky) {
|
|
13
|
+
(IntentKind::RepairRequest, DirtyDiffReadiness::HarnessRefreshOnly, false) => {
|
|
14
|
+
harness_refresh_policy()
|
|
15
|
+
}
|
|
16
|
+
(
|
|
17
|
+
IntentKind::RepairRequest,
|
|
18
|
+
DirtyDiffReadiness::HarnessRefreshWithUnrelatedDirty,
|
|
19
|
+
false,
|
|
20
|
+
) => harness_refresh_policy(),
|
|
21
|
+
(IntentKind::NewTask, DirtyDiffReadiness::HarnessRefreshOnly, false) => {
|
|
22
|
+
POLICY_DIRTY_NEW_REFRESH
|
|
23
|
+
}
|
|
24
|
+
(IntentKind::NewTask, DirtyDiffReadiness::HarnessRefreshWithUnrelatedDirty, false) => {
|
|
25
|
+
POLICY_DIRTY_NEW_ISOLATED
|
|
26
|
+
}
|
|
27
|
+
(IntentKind::NewTask, _, false) => POLICY_DIRTY_NEW_WORKTREE,
|
|
28
|
+
(IntentKind::CommitRequest, _, false) => POLICY_DIRTY_COMMIT_USER,
|
|
29
|
+
_ => POLICY_DIRTY_BLOCK,
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
pub(crate) fn baseline_policy(
|
|
34
|
+
intent: IntentKind,
|
|
35
|
+
dirty: DirtyDiffReadiness,
|
|
36
|
+
risky: bool,
|
|
37
|
+
) -> Policy {
|
|
38
|
+
match intent {
|
|
39
|
+
IntentKind::NoCommitRequest => POLICY_BASELINE_NO_COMMIT,
|
|
40
|
+
IntentKind::ReviewRequest => POLICY_BASELINE_REVIEW,
|
|
41
|
+
IntentKind::CancelRequest => POLICY_BASELINE_CANCEL,
|
|
42
|
+
IntentKind::CommitRequest | IntentKind::RepairRequest
|
|
43
|
+
if dirty == DirtyDiffReadiness::HarnessRefreshOnly =>
|
|
44
|
+
{
|
|
45
|
+
harness_refresh_policy()
|
|
46
|
+
}
|
|
47
|
+
IntentKind::CommitRequest | IntentKind::RepairRequest => POLICY_BASELINE_COMMIT_UPGRADE,
|
|
48
|
+
IntentKind::NewTask if dirty == DirtyDiffReadiness::HarnessRefreshOnly && !risky => {
|
|
49
|
+
POLICY_BASELINE_NEW_REFRESH
|
|
50
|
+
}
|
|
51
|
+
IntentKind::NewTask if !risky => POLICY_BASELINE_NEW_UPGRADE,
|
|
52
|
+
IntentKind::NewTask => unsafe_policy(),
|
|
53
|
+
_ => POLICY_BASELINE_BLOCK,
|
|
54
|
+
}
|
|
55
|
+
}
|