refactor(goal): delete the natural-language /goal prose parser (determinism audit batch 2, #3)

The host parsed ten prose phrasings plus a clause allow-list ("make it your
/goal to ...") into durable goals before the provider call. Per the founder
direction the model decides when a goal is useful: prose asks now reach the
model, which calls create_goal; the deterministic user path is the leading
/goals <objective> command, unchanged. Same philosophy as 4de9e9e28, which
deleted the Operate verb-list promotion.

Tests: tools::goal 29 passed 0 failed; engine goal suites (rewritten: prose
never activates, seeded-goal flows intact) 12 passed 0 failed. Clippy
--all-targets, fmt, dead-code budget clean.
This commit is contained in:
CodeWhale Bot committed 2026-09-17 16:40:58 -07:00
1 parent 72b028e5ee
commit 27ae676972
3 files changed
+64 -246

No files matched your search

+12 -59
View File
@@ -40,8 +40,7 @@ use crate::route_runtime::{
ResolvedRuntimeRoute, ValidatedRuntimeRoute, resolve_runtime_route_for_identity,
};
use crate::tools::goal::{
GoalPauseReason, GoalSnapshot, GoalStatus, SharedGoalState, explicit_goal_directive,
new_shared_goal_state,
GoalPauseReason, GoalSnapshot, GoalStatus, SharedGoalState, new_shared_goal_state,
};
use crate::tools::plan::{SharedPlanState, new_shared_plan_state};
use crate::tools::shell::{SharedShellManager, new_shared_shell_manager};
@@ -4853,66 +4852,20 @@ impl Engine {
if autonomous && self.cancel_token.is_cancelled() {
return SendMessageOutcome::NotStarted { error: None };
}
let mut goal_objective = goal_objective;
let mut goal_token_budget = goal_token_budget;
let mut goal_status = goal_status;
let initial_usage_owner = compaction.runtime_cost_owner.clone();
// A literal natural-language `/goal` declaration is control-plane
// intent, not a suggestion that each provider may acknowledge or
// ignore. Activate it through the same GoalState::create path as the
// model-visible create_goal tool before constructing any provider
// request. Only structurally external user input can authorize this;
// runtime text, recalled memory, handoffs, and pasted multi-line
// transcripts cannot create a goal.
// Goals are created by the model (`create_goal`) or by the leading
// `/goal <objective>` command; the host never infers one from
// wording (docs/design/TUI_DECONSTRUCTION.md — founder clarification
// 2026-09-09: the model decides when a goal is useful). The
// natural-language `/goal` prose parser that used to recognize
// "make it your /goal to ..." is gone with the #6290 rework — a
// prose ask reaches the model, which calls `create_goal` when a goal
// is actually useful.
//
// Goals are created by the model (`create_goal`) or by this literal
// user declaration; the host never infers one from wording. The
// verb-list promotion that used to turn ordinary Operate prompts into
// goals is gone (docs/design/TUI_DECONSTRUCTION.md — founder
// clarification 2026-09-09: the model decides when a goal is useful).
// `GoalState::create` still refuses while an unfinished goal exists,
// so a paused or blocked goal is never silently replaced.
//
// KV-cache effect: this only selects the already-existing volatile
// <session_goal> contributor. It adds no new stable-prefix text.
let goal_request = if provenance.can_authorize_work() {
explicit_goal_directive(&content)
} else {
None
};
if let Some(directive) = goal_request {
let result = self
.config
.goal_state
.lock()
.map_err(|_| "goal state lock poisoned".to_string())
.and_then(|mut state| {
state
.create(directive.objective, None)
.map_err(str::to_string)?;
Ok(state.snapshot())
});
match result {
Ok(snapshot) => {
goal_objective.clone_from(&snapshot.objective);
goal_token_budget = snapshot.token_budget;
goal_status = GoalStatus::Active;
// Publish before TurnStarted/provider dispatch so the TUI
// and durable runtime host observe the real goal action,
// even when this model would otherwise reply only in prose.
let _ = self.tx_event.send(Event::GoalUpdated { snapshot }).await;
}
Err(error) => {
let _ = self
.tx_event
.send(Event::status(format!(
"Requested /goal was not created: {error}"
)))
.await;
}
}
}
// KV-cache effect: none. Goal state still flows through the existing
// volatile <session_goal> contributor; nothing here touches the
// stable prefix.
let effective_provider = route.identity.provider;
let provider_identity = route.identity.key.clone();
+52 -43
View File
@@ -3160,7 +3160,7 @@ fn goal_custom_route_config() -> Config {
}
#[tokio::test]
async fn explicit_natural_goal_activates_before_provider_request() {
async fn ordinary_prose_never_activates_a_goal() {
let request_entered = std::sync::Arc::new(tokio::sync::Notify::new());
let release_request = std::sync::Arc::new(tokio::sync::Notify::new());
let model = std::sync::Arc::new(FirstRequestGatedGoalModelClient {
@@ -3213,24 +3213,25 @@ async fn explicit_natural_goal_activates_before_provider_request() {
.await
.expect("send explicit natural goal turn");
let mut saw_goal_before_turn = false;
// #6290 rework: the natural-language `/goal` prose parser is gone. The
// same wording that used to activate a goal is now an ordinary turn;
// only the model (`create_goal`) or the `/goal` command creates one.
let mut saw_goal = false;
loop {
let event = tokio::time::timeout(model_turn_event_timeout(), async {
handle.rx_event.write().await.recv().await
})
.await
.expect("explicit goal event timeout")
.expect("explicit goal event");
.expect("prose goal event timeout")
.expect("prose goal event");
match event {
Event::GoalUpdated { snapshot } => {
assert_eq!(snapshot.objective.as_deref(), Some("solve navier stokes"));
assert_eq!(snapshot.status, "active");
saw_goal_before_turn = true;
Event::GoalUpdated { .. } => {
saw_goal = true;
}
Event::TurnStarted { .. } => {
assert!(
saw_goal_before_turn,
"durable goal must be published before provider work starts"
!saw_goal,
"ordinary prose must not publish a goal before provider work starts"
);
break;
}
@@ -3241,29 +3242,15 @@ async fn explicit_natural_goal_activates_before_provider_request() {
tokio::time::timeout(model_turn_event_timeout(), request_entered.notified())
.await
.expect("provider request was never entered");
let snapshot = goal_state.lock().expect("goal lock").snapshot();
assert_eq!(snapshot.objective.as_deref(), Some("solve navier stokes"));
assert!(snapshot.is_active());
// Stop autonomous continuation after the one provider-boundary receipt.
handle
.send(Op::SetGoalStatus {
goal_id: None,
status: crate::tools::goal::GoalStatus::Paused,
clear: false,
})
.await
.expect("queue goal pause");
release_request.notify_one();
let _ = tokio::time::timeout(model_turn_event_timeout(), handle.get_session_snapshot())
.await
.expect("goal pause did not settle")
.expect("post-goal session snapshot");
.expect("turn did not settle")
.expect("post-turn session snapshot");
let snapshot = goal_state.lock().expect("goal lock").snapshot();
assert_eq!(snapshot.objective.as_deref(), None);
assert!(!snapshot.is_active());
assert_eq!(model.calls.load(std::sync::atomic::Ordering::SeqCst), 1);
assert_eq!(
goal_state.lock().expect("goal lock").snapshot().status,
"paused"
);
handle.send(Op::Shutdown).await.expect("shutdown engine");
run_task.await.expect("engine task");
@@ -3395,11 +3382,16 @@ async fn operate_never_promotes_wording_to_a_goal() {
assert!(!active);
assert_eq!(contracts, 0, "Work never sees the Operate contract");
// An explicit declaration still creates one, through the same path.
// #6290 rework: even an explicit-looking declaration is ordinary
// prose now — the host never parses it, and the model decides goals
// through `create_goal`.
let (objective, active, contracts) =
operate_goal_probe(AppMode::Operate, "Please set /goal to ship the release").await;
assert_eq!(objective.as_deref(), Some("ship the release"));
assert!(active, "an explicit declaration must create the goal");
assert_eq!(
objective, None,
"prose asking for a goal must not create one host-side"
);
assert!(!active);
assert_eq!(contracts, 1);
}
@@ -3507,9 +3499,20 @@ async fn operate_contract_is_appended_once_and_an_existing_goal_is_never_replace
let first_objective =
"Migrate the settings loader to the new config crate and keep the old keys readable";
let first = format!("Please set /goal to {first_objective}");
// #6290 rework: prose no longer creates goals, so the unfinished goal
// this test needs is seeded directly — the same `GoalState::create` path
// the `/goal` command and the model's `create_goal` tool use.
goal_state
.lock()
.expect("goal lock")
.create(first_objective.to_string(), None)
.expect("seed unfinished goal");
handle
.send(send(&first, None, crate::tools::goal::GoalStatus::Active))
.send(send(
first_objective,
Some(first_objective.to_string()),
crate::tools::goal::GoalStatus::Active,
))
.await
.expect("send first Operate turn");
tokio::time::timeout(model_turn_event_timeout(), first_entered.notified())
@@ -12372,10 +12375,10 @@ async fn operate_model_shell_uses_normal_approval_and_workspace_sandbox() {
"\"finish_reason\":\"stop\"}]}\n\n",
"data: [DONE]\n\n",
);
// Operate no longer infers a goal from wording, so this fixture declares
// one explicitly; after the approved shell runs, the model seals it
// through the same `update_goal` tool a live Operate turn uses, and only
// then does the final "done" arrive.
// The goal this fixture seals is seeded directly (prose no longer
// creates goals since the #6290 rework); after the approved shell runs,
// the model seals it through the same `update_goal` tool a live Operate
// turn uses, and only then does the final "done" arrive.
let goal_seal_marker = "goal-seal-receipt-0902";
let goal_sse = concat!(
"data: {\"id\":\"chatcmpl-operate-goal\",\"choices\":[{\"index\":0,\"delta\":{\"tool_calls\":[",
@@ -12455,19 +12458,26 @@ async fn operate_model_shell_uses_normal_approval_and_workspace_sandbox() {
},
&api_config,
);
engine
.config
.goal_state
.lock()
.expect("goal lock")
.create("write the requested local fixture".to_string(), None)
.expect("seed fixture goal");
let handle_for_approval = handle.clone();
let run_task = tokio::spawn(engine.run());
handle
.send(Op::SendMessage(TurnSpec {
max_output_tokens: None,
content: "Please set /goal to write the requested local fixture".to_string(),
content: "Write the requested local fixture to the workspace".to_string(),
images: Vec::new(),
mode: AppMode::Operate,
route: resolved_route_for_test(&api_config, crate::config::DEFAULT_TEXT_MODEL),
compaction: Box::new(CompactionConfig::default()),
initial_routed_usage: Box::default(),
goal_objective: None,
goal_objective: Some("write the requested local fixture".to_string()),
goal_token_budget: None,
goal_status: crate::tools::goal::GoalStatus::Active,
reasoning_effort: None,
@@ -12499,9 +12509,8 @@ async fn operate_model_shell_uses_normal_approval_and_workspace_sandbox() {
Event::ApprovalRequired { id, tool_name, .. } => {
saw_approval = true;
assert_eq!(tool_name, "Bash");
// No goal is created for this prompt: goals are model-decided
// (`create_goal`), so there is no goal-continuation loop to
// pause in this mock.
// The seeded fixture goal is orthogonal to this gate:
// Operate uses the normal approval flow either way.
handle_for_approval
.approve_tool_call(id)
.await
-144
View File
@@ -44,116 +44,6 @@ pub fn new_shared_goal_state_from_snapshot(snapshot: &GoalSnapshot) -> SharedGoa
Arc::new(Mutex::new(GoalState::from_snapshot(snapshot)))
}
/// A goal declaration stated in ordinary user prose rather than as a leading
/// `/goal <objective>` command.
///
/// This intentionally recognizes only a narrow, explicit `/goal` directive.
/// Ordinary long-running requests are not silently promoted to goals, and a
/// quoted multi-line transcript cannot authorize one through a later line.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ExplicitGoalDirective {
pub objective: String,
}
/// Extract an explicit natural-language `/goal` declaration from the user's
/// first non-empty line.
///
/// The provider never participates in this decision. That makes an explicit
/// declaration behave the same on DeepSeek, Zai, and every compatible route,
/// while keeping ordinary tasks and discussion *about* `/goal` untouched.
#[must_use]
pub fn explicit_goal_directive(input: &str) -> Option<ExplicitGoalDirective> {
let line = input.lines().find(|line| !line.trim().is_empty())?.trim();
let lower = line.to_ascii_lowercase();
// Objective follows the directive.
for pattern in [
"make it your /goal to",
"make this your /goal to",
"make that your /goal to",
"set your /goal to",
"set my /goal to",
"set the /goal to",
"set /goal to",
"your /goal is",
"my /goal is",
"the /goal is",
] {
let Some(index) = lower.find(pattern) else {
continue;
};
if !is_direct_goal_clause(&lower[..index]) {
continue;
}
let objective = normalize_explicit_goal_objective(&line[index + pattern.len()..])?;
return Some(ExplicitGoalDirective { objective });
}
// Objective precedes the marker: "make solving X your /goal".
for marker in [" your /goal", " my /goal", " the /goal"] {
let Some(marker_index) = lower.find(marker) else {
continue;
};
let before_marker = &lower[..marker_index];
let Some(make_index) = before_marker.rfind("make ") else {
continue;
};
if !is_direct_goal_clause(&lower[..make_index]) {
continue;
}
let objective =
normalize_explicit_goal_objective(&line[make_index + "make ".len()..marker_index])?;
if matches!(
objective.to_ascii_lowercase().as_str(),
"it" | "this" | "that"
) {
continue;
}
return Some(ExplicitGoalDirective { objective });
}
None
}
fn is_direct_goal_clause(prefix: &str) -> bool {
let prefix = prefix.trim_end();
if prefix.is_empty() {
return true;
}
[
"please",
"and",
"then",
"also",
"now",
"you to",
"need to",
"can you",
"could you",
"would you",
"can we",
"could we",
"should",
"must",
"-",
";",
":",
",",
]
.iter()
.any(|ending| prefix.ends_with(ending))
}
fn normalize_explicit_goal_objective(raw: &str) -> Option<String> {
let objective = raw
.trim()
.trim_start_matches([':', '-', '–', '—'])
.trim()
.trim_end_matches(['.', '!', '?'])
.trim();
(!objective.is_empty()).then(|| objective.to_string())
}
/// Runtime status for a goal.
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
pub enum GoalStatus {
@@ -1301,40 +1191,6 @@ mod tests {
use super::*;
#[test]
fn explicit_goal_directive_extracts_user_requested_objective() {
let request = explicit_goal_directive(
"hello - take over and make it your /goal to solve navier stokes",
)
.expect("explicit /goal request");
assert_eq!(request.objective, "solve navier stokes");
let request = explicit_goal_directive("Please make ship the release your /goal.")
.expect("objective-before-marker request");
assert_eq!(request.objective, "ship the release");
let request = explicit_goal_directive("Could you set /goal to repair provider routing?")
.expect("set /goal request");
assert_eq!(request.objective, "repair provider routing");
}
#[test]
fn explicit_goal_directive_rejects_ordinary_or_quoted_goal_discussion() {
for input in [
"solve navier stokes",
"why didn't you make it your /goal to solve navier stokes?",
"what does /goal do?",
"review the /goal implementation",
"see this transcript where /goal was ignored:\nhello - take over and make it your /goal to solve navier stokes",
] {
assert_eq!(
explicit_goal_directive(input),
None,
"must not activate from: {input}"
);
}
}
#[tokio::test]
async fn update_goal_rejects_objective_knob_instead_of_ignoring_it() {
// #5123-class: `objective` used to return a success receipt with no