#[test]
fn production_workflow_uses_an_llm_loop_contract_without_a_precomputed_rule_plan() {
let args = super::deep_research_workflow_args_with_scope(
"A semantically ambiguous question",
false,
super::DeepResearchEvidenceScope::WebAndWorkspace,
);
let contract = &args["input"]["loop_contract"];
assert_eq!(contract["pattern"], "adaptive-deep-research");
assert_eq!(contract["maker_role"], "evidence-researcher");
assert_eq!(contract["checker_role"], "evidence-coverage-checker");
assert_eq!(contract["planner"]["agent"], "loop-planner");
assert_eq!(contract["planner"]["timeout_ms"], 120_000);
assert_eq!(contract["checker"]["timeout_ms"], 180_000);
assert!(contract["planner"]["output_schema"]["properties"]
.get("intent_summary")
.is_none());
assert!(
contract["planner"]["output_schema"]["properties"]["budget"]["properties"]
.get("per_task_timeout_secs")
.is_none()
);
assert_eq!(
contract["planner"]["output_schema"]["properties"]["tracks"]["items"]["type"],
"string"
);
assert!(contract["planner"]["output_schema"]["properties"]
.get("plan_rationale")
.is_none());
assert_eq!(
contract["planner"]["output_schema"]["properties"]["tracks"]["maxItems"],
4
);
assert_eq!(
contract["planner"]["output_schema"]["properties"]["budget"]["properties"]
["max_steps_per_task"]["maximum"],
2
);
assert_eq!(contract["checker"]["agent"], "loop-checker");
assert!(contract["planner"]["prompt"]
.as_str()
.is_some_and(|prompt| prompt
.contains("Never infer stages, depth, route, or budget from keyword counts")));
assert!(contract["planner"]["output_schema"]["properties"]["phases"].is_object());
assert_eq!(
contract["planner"]["output_schema"]["properties"]["phases"]["items"]["type"],
"string"
);
assert_eq!(
contract["planner"]["output_schema"]["properties"]["execution_route"]["enum"],
serde_json::json!([
"direct_only",
"direct_then_review",
"direct_then_maker",
"maker_first"
])
);
assert!(contract["planner"]["output_schema"]["properties"]
.get("strategy")
.is_none());
assert_eq!(
contract["planner"]["output_schema"]["properties"]["report_title"]["type"],
"string"
);
assert_eq!(
contract["planner"]["output_schema"]["properties"]["workspace_evidence_required"]["type"],
"boolean"
);
assert!(contract["planner"]["prompt"]
.as_str()
.is_some_and(|prompt| prompt.contains(
"explicitly asks about this repository, a local codebase, or attached/local artifacts"
)));
assert!(contract["planner"]["prompt"]
.as_str()
.is_some_and(|prompt| prompt.contains("direct_then_review")));
assert!(contract["planner"]["prompt"]
.as_str()
.is_some_and(|prompt| prompt.contains("same language as the query")));
assert!(contract["planner"]["prompt"]
.as_str()
.is_some_and(|prompt| prompt.contains("primary or authoritative evidence")));
assert!(contract["planner"]["prompt"]
.as_str()
.is_some_and(|prompt| prompt.contains("every explicitly requested alternative")));
assert!(contract["planner"]["prompt"]
.as_str()
.is_some_and(|prompt| prompt.contains("directly fetchable")));
assert!(contract["planner"]["prompt"]
.as_str()
.is_some_and(|prompt| prompt
.contains("total number of evidence passes including initial collection")));
assert!(contract["planner"]["output_schema"]["properties"]["search_queries"].is_object());
assert_eq!(
contract["planner"]["output_schema"]["properties"]["search_queries"]["maxItems"],
4
);
assert_eq!(
contract["planner"]["output_schema"]["properties"]["budget"]["properties"]
["direct_fetches"]["maximum"],
8
);
assert!(contract["planner"]["output_schema"]["properties"]
.get("source_targets")
.is_none());
assert!(contract["planner"]["output_schema"]["properties"]["seed_urls"].is_object());
assert!(contract["planner"]["output_schema"]["properties"]["budget"].is_object());
assert_eq!(
contract["planner"]["output_schema"]["properties"]["budget"]["properties"]
["synthesis_timeout_secs"]["minimum"],
120
);
assert_eq!(
contract["planner"]["output_schema"]["properties"]["budget"]["properties"]
["synthesis_timeout_secs"]["maximum"],
180
);
assert_eq!(contract["hard_caps"]["synthesis_timeout_ms"], 180_000);
assert_eq!(
contract["checker"]["output_schema"]["properties"]["next_action"]["enum"],
serde_json::json!(["none", "direct_retrieval", "maker"])
);
assert!(contract["checker"]["output_schema"]["properties"]["report_summary"].is_object());
assert!(contract["checker"]["output_schema"]["properties"]["verified_findings"].is_object());
assert!(contract["checker"]["output_schema"]["properties"]["track_assessments"].is_object());
assert!(
contract["checker"]["output_schema"]["properties"]["stop_condition_assessments"]
.is_object()
);
assert_eq!(
contract["checker"]["output_schema"]["properties"]["track_assessments"]["items"]
["properties"]["status"]["enum"],
serde_json::json!(["supported", "bounded", "uncovered"])
);
assert!(contract["checker"]["output_schema"]["required"]
.as_array()
.is_some_and(
|required| required.contains(&serde_json::json!("track_assessments"))
&& required.contains(&serde_json::json!("stop_condition_assessments"))
));
assert!(args["input"].get("research_plan").is_none());
assert!(args["source"].as_str().is_some_and(|source| {
source.contains("engineered_loop_enabled: engineeredLoopEnabled")
&& source.contains("return await collectDirectWebResearch()")
&& source.contains("const localMinSuccessCount")
&& source.contains("Authoritative scope: web_only")
&& source.contains("minItems: 1")
&& source.contains("const plannerWorkflowRetry")
&& source.contains("max_attempts: 1")
&& source.contains("const checkerWorkflowRetry = {max_attempts: 1")
&& source.contains("step_name: \"generate_object\"")
&& source.contains("schema_name: \"deep_research_plan\"")
&& source.contains("schema_name: \"deep_research_check\"")
&& source.contains("normalizePlannerBudget")
&& source.contains("ctx.tool(\"batch\"")
&& source.contains("batchOutputSections")
&& source.contains("tool: \"web_search\"")
&& source.contains("tool: \"web_fetch\"")
&& source.contains("directIteration === 0")
&& source.contains("excluded_urls")
&& source.contains("retrieval_elapsed_ms")
&& source.contains("retrievalBudgetUsedMs")
&& source.contains("plannerObservedLatencyMs")
&& source.contains("observedCheckerLatencyMs")
&& !source.contains("plannedSynthesisTimeoutMs")
&& source.contains("plannerStructuredMode")
&& source.contains("packMakerTracks")
&& source.contains("checkerReserveMs")
&& source.contains("promptMakerReserveMs")
&& source.contains("makerAndCheckerFloorMs")
&& source.contains("step_elapsed_ms")
&& source.contains("plannedSeedEvidenceContext")
&& source.contains("researchPlan.execution_route === \"direct_then_maker\"")
&& source.contains("return the existing source-backed evidence without a tool call")
&& source.contains("hasReusableEvidencePackage")
&& source.contains("Public web gaps use direct_retrieval")
&& source.contains("Findings state facts")
&& source.contains("A URL, title, or search snippet alone is a discovery lead")
&& source.contains("discovery_leads_not_evidence")
&& source.contains("raw.githubusercontent.com")
&& source.contains("raw.githubusercontent.com/wiki")
&& source.contains("directIteration + 1 < maxResearchRounds")
&& source.contains("exact supporting source URL")
&& source.contains("validateCheckerDecision")
&& source.contains("finalize_gate_passed")
&& !source.contains("researchPlan.answer_shape ===")
}));
assert_eq!(
contract["checker"]["output_schema"]["properties"]["report_summary"]["maxLength"],
4800
);
}
#[test]
fn llm_plan_controls_synthesis_timeout_and_visible_status() {
let output = serde_json::json!({
"plan": {
"answer_shape": "briefing",
"budget": {
"synthesis_timeout_ms": 42000,
"max_iterations": 2,
"max_parallel_tasks": 3,
"retrieval_timeout_ms": 75000
}
}
})
.to_string();
assert_eq!(
super::deep_research_planned_synthesis_timeout_ms(Some(&output)),
Some(42_000)
);
let status = super::deep_research_plan_status(&output).unwrap();
assert!(status.contains("briefing"), "{status}");
assert!(status.contains("≤2 iterations"), "{status}");
assert!(status.contains("75s retrieval"), "{status}");
for (planned, expected) in [(5_000, 10_000), (180_000, 180_000), (250_000, 180_000)] {
let output = serde_json::json!({
"plan": { "budget": { "synthesis_timeout_ms": planned } }
})
.to_string();
assert_eq!(
super::deep_research_planned_synthesis_timeout_ms(Some(&output)),
Some(expected),
"the host must preserve the planner clock up to the shared synthesis ceiling"
);
}
}