diff --git a/assets/agents/deep-research/graph.yaml b/assets/agents/deep-research/graph.yaml index 757e668..d9d375e 100644 --- a/assets/agents/deep-research/graph.yaml +++ b/assets/agents/deep-research/graph.yaml @@ -38,7 +38,7 @@ initial_state: research_attempts: 0 local_context: '' local_sources: '' - # Fault-path defaults (TASK-023 hardening). `questions: []` also keeps the + # Fault-path defaults. `questions: []` also keeps the # knowledge_lookup lane alive for one super-step when `plan` dies inside the # static fan-out: research_each_question maps over zero items while the # plan_fault → end_fault route renders the failure sentinel. @@ -100,7 +100,7 @@ nodes: description: 3-5 specific, self-contained sub-questions. required: [research_plan, questions] # On success plan_failure holds the node's structured JSON output; on - # failure the engine writes "LLM node …failed: " into it BEFORE + # failure the engine writes "LLM node ...failed: " into it BEFORE # routing to the fallback (schema properties are only merged on success). state_updates: plan_failure: '{{output}}' @@ -175,7 +175,7 @@ nodes: max_attempts: 2 state_updates: finding: '{{output}}' - # On failure the engine writes "LLM node …failed: " into `finding` + # On failure the engine writes "LLM node ...failed: " into `finding` # via the state_updates above BEFORE routing to the fallback. The marker # is branch-local: a map branch's fallback must stay inside the branch. fallback: question_fault diff --git a/src/config/request_context.rs b/src/config/request_context.rs index f985b9c..cd7eda8 100644 --- a/src/config/request_context.rs +++ b/src/config/request_context.rs @@ -8157,9 +8157,6 @@ mod tests { assert!(paths::skill_file("frontend-ui-ux").exists()); } - /// TASK-021 pinned prose: the task-queue guidance added to the shipped - /// artifacts must not silently erode. Each anchor quotes the shipped text - /// verbatim (chosen to avoid line-wrap boundaries in the YAML sources). #[test] #[serial] fn bundled_assets_pin_task_queue_guidance() { diff --git a/src/graph/agent.rs b/src/graph/agent.rs index fc8edde..85ee83e 100644 --- a/src/graph/agent.rs +++ b/src/graph/agent.rs @@ -20,8 +20,6 @@ const DEFAULT_TIMEOUT_SECS: u64 = 300; #[derive(Debug, PartialEq, Eq)] pub(super) enum AgentExecutionOutcome { - /// Carries the raw agent output for tests; the executor's agent arm - /// ignores the payload. Continue(String), FellBack(String), } @@ -49,8 +47,8 @@ impl AgentNodeExecutor { } /// Turns the node run's final result into a routing outcome. With a -/// `fallback` declared, any failure — retries exhausted, hard error, or -/// extraction failure — is written into state as +/// `fallback` declared, any failure (e.g. retries exhausted, hard error, or +/// extraction failure) is written into state as /// `"Agent node failed: "` and routes to the fallback. Only the /// node's `state_updates` can capture that string (bound as `{{output}}` /// during interpolation); the output-schema auto-merge never applies to it, @@ -192,11 +190,11 @@ fn boxed_attempt<'a>(fut: impl Future> + Send + 'a) -> A /// failures that `is_transient_error` recognizes. /// /// `run_agent_for_graph` takes the peer identity off the ctx and never -/// restores it, so BOTH halves — the (id, inbox) assignment and the registry -/// — are captured up front and re-armed before every retry; re-arming only +/// restores it, so BOTH halves, the (id, inbox) assignment and the registry, +/// are captured up front and re-armed before every retry; re-arming only /// the assignment would give a retried attempt its identity back but no /// roster and a broken `agent__send_message`. A frontier peer is retired -/// exactly once, after the final attempt, the moment the agent stops — not +/// exactly once, after the final attempt, the moment the agent stops, not /// after extraction. Chains re-arm the same identity for later steps and /// leave retirement to the chain runner. async fn run_with_retries( @@ -259,8 +257,8 @@ async fn retry_transient( } /// Applies the wall-clock bound and the human-readable failure contexts to a -/// single attempt. The contexts land here — before the caller inspects the -/// error — because `tokio::time::error::Elapsed` renders as "deadline has +/// single attempt. The contexts land here, before the caller inspects the +/// error, because `tokio::time::error::Elapsed` renders as "deadline has /// elapsed", which the transient matcher would not recognize as a timeout. async fn bounded_attempt( agent_name: &str, diff --git a/src/graph/map.rs b/src/graph/map.rs index 3e35032..09fb027 100644 --- a/src/graph/map.rs +++ b/src/graph/map.rs @@ -294,8 +294,8 @@ async fn run_chain_step( branch_nodes.sort_unstable(); bail!( "routed to '{target}' which is outside the branch subgraph rooted at \ - '{}' (branch nodes: {}). Routing targets inside a map branch — script \ - `_next` or node fallback — must stay within the branch.", + '{}' (branch nodes: {}). Routing targets inside a map branch (script \ + `_next` or node fallback) must stay within the branch.", chain.entry, branch_nodes.join(", ") ); @@ -1282,7 +1282,7 @@ nodes: ); assert!( chain.contains( - "Routing targets inside a map branch — script `_next` or node fallback — \ + "Routing targets inside a map branch (script `_next` or node fallback) \ must stay within the branch." ), "{chain}" diff --git a/src/graph/mod.rs b/src/graph/mod.rs index d5cc6e8..5fb1896 100644 --- a/src/graph/mod.rs +++ b/src/graph/mod.rs @@ -23,6 +23,7 @@ pub use executor::GraphExecutor; pub use parser::{GraphParser, agent_has_graph}; use serde_json::Value; use std::time::Duration; +use anyhow::Error; pub use types::{Graph, NodeType}; pub const GRAPH_SCHEMA_VERSION: &str = "1.0"; @@ -34,12 +35,7 @@ pub(crate) fn wall_clock(secs: u64) -> Option { (secs != 0).then(|| Duration::from_secs(secs)) } -/// Whether an error looks like a transient transport/provider failure worth -/// retrying. Typed transport errors anywhere in the chain (reqwest -/// connect/timeout failures and tokio's `Elapsed`) are recognized first; -/// providers that surface failures only as rendered text fall back to -/// substring matching over the whole context chain. -pub(crate) fn is_transient_error(err: &anyhow::Error) -> bool { +pub(crate) fn is_transient_error(err: &Error) -> bool { if err.chain().any(|c| { c.downcast_ref::() .is_some_and(|e| e.is_connect() || e.is_timeout()) @@ -136,7 +132,7 @@ mod tests { let elapsed = tokio::time::timeout(Duration::from_secs(1), std::future::pending::<()>()) .await .expect_err("pending future must time out"); - let err = anyhow::Error::new(elapsed).context("calling provider"); + let err = Error::new(elapsed).context("calling provider"); // "deadline has elapsed" contains no recognized substring, so only // the typed pre-check can classify this as transient. @@ -157,6 +153,6 @@ mod tests { .expect_err("closed local port must refuse the connection"); assert!(err.is_connect()); - assert!(is_transient_error(&anyhow::Error::new(err))); + assert!(is_transient_error(&Error::new(err))); } } diff --git a/src/graph/validator.rs b/src/graph/validator.rs index 297c406..bf65f99 100644 --- a/src/graph/validator.rs +++ b/src/graph/validator.rs @@ -416,7 +416,7 @@ impl GraphValidator { node_id, format!( "{kind} node declares `fallback` with `output_schema` but no \ - `state_updates`; on failure the \"…failed: \" string is \ + `state_updates`; on failure the \"...failed: \" string is \ only written through `state_updates`, so the fallback node \ cannot see why it was reached" ),