mirror of
https://github.com/instructkr/claw-code.git
synced 2026-04-08 17:14:49 +08:00
test(tools): end-to-end stall-detect and recovery loop coverage
Proves the clawhip restart/recover flow that gaebal-gajae flagged: 1. stall_detect_and_resolve_trust_end_to_end - Worker created without trusted_roots -> trust_auto_resolve=false - WorkerObserve with trust-prompt text -> status=trust_required, gate cleared=false - WorkerResolveTrust -> status=spawning, trust_gate_cleared=true - WorkerObserve with ready text -> status=ready_for_prompt Full resolve path verified end-to-end. 2. stall_detect_and_restart_recovery_end_to_end - Worker stalls at trust_required - WorkerRestart resets to spawning, trust_gate_cleared=false Documents the restart-then-re-acquire-trust flow. Note: seconds_since_update is in .claw/worker-state.json (state file), not in the Worker tool output struct. Staleness detection via state file is covered by emit_state_file_writes_worker_status_on_transition in worker_boot.rs tests. 87 tool tests passing, 0 failing.
This commit is contained in:
@@ -5618,6 +5618,101 @@ mod tests {
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn stall_detect_and_resolve_trust_end_to_end() {
|
||||
// 1. Create worker WITHOUT trusted_roots so trust won't auto-resolve
|
||||
let created = execute_tool(
|
||||
"WorkerCreate",
|
||||
&json!({"cwd": "/no/trusted/root/here"}),
|
||||
)
|
||||
.expect("WorkerCreate should succeed");
|
||||
let created_output: serde_json::Value = serde_json::from_str(&created).expect("json");
|
||||
let worker_id = created_output["worker_id"].as_str().expect("worker_id").to_string();
|
||||
assert_eq!(created_output["trust_auto_resolve"], false);
|
||||
|
||||
// 2. Observe trust prompt screen text -> worker stalls at trust_required
|
||||
let stalled = execute_tool(
|
||||
"WorkerObserve",
|
||||
&json!({
|
||||
"worker_id": worker_id,
|
||||
"screen_text": "Do you trust the files in this folder?\n[Allow] [Deny]"
|
||||
}),
|
||||
)
|
||||
.expect("WorkerObserve should succeed");
|
||||
let stalled_output: serde_json::Value = serde_json::from_str(&stalled).expect("json");
|
||||
assert_eq!(
|
||||
stalled_output["status"], "trust_required",
|
||||
"worker should stall at trust_required when trust prompt seen without allowlist"
|
||||
);
|
||||
assert_eq!(stalled_output["trust_gate_cleared"], false);
|
||||
// 3. Clawhip calls WorkerResolveTrust to unblock
|
||||
let resolved = execute_tool(
|
||||
"WorkerResolveTrust",
|
||||
&json!({"worker_id": worker_id}),
|
||||
)
|
||||
.expect("WorkerResolveTrust should succeed");
|
||||
let resolved_output: serde_json::Value = serde_json::from_str(&resolved).expect("json");
|
||||
assert_eq!(
|
||||
resolved_output["status"], "spawning",
|
||||
"worker should return to spawning after trust resolved"
|
||||
);
|
||||
assert_eq!(resolved_output["trust_gate_cleared"], true);
|
||||
|
||||
// 4. Ready screen text now advances worker normally
|
||||
let ready = execute_tool(
|
||||
"WorkerObserve",
|
||||
&json!({
|
||||
"worker_id": worker_id,
|
||||
"screen_text": "Ready for input\n>"
|
||||
}),
|
||||
)
|
||||
.expect("WorkerObserve should succeed after trust resolved");
|
||||
let ready_output: serde_json::Value = serde_json::from_str(&ready).expect("json");
|
||||
assert_eq!(
|
||||
ready_output["status"], "ready_for_prompt",
|
||||
"worker should reach ready_for_prompt after trust resolved and ready screen seen"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn stall_detect_and_restart_recovery_end_to_end() {
|
||||
// Worker stalls at trust_required, clawhip restarts instead of resolving
|
||||
let created = execute_tool(
|
||||
"WorkerCreate",
|
||||
&json!({"cwd": "/no/trusted/root/restart-test"}),
|
||||
)
|
||||
.expect("WorkerCreate should succeed");
|
||||
let created_output: serde_json::Value = serde_json::from_str(&created).expect("json");
|
||||
let worker_id = created_output["worker_id"].as_str().expect("worker_id").to_string();
|
||||
|
||||
// Force trust_required
|
||||
let stalled = execute_tool(
|
||||
"WorkerObserve",
|
||||
&json!({
|
||||
"worker_id": worker_id,
|
||||
"screen_text": "trust this folder? [Yes] [No]"
|
||||
}),
|
||||
)
|
||||
.expect("WorkerObserve should succeed");
|
||||
let stalled_output: serde_json::Value = serde_json::from_str(&stalled).expect("json");
|
||||
assert_eq!(stalled_output["status"], "trust_required");
|
||||
|
||||
// WorkerRestart resets the worker
|
||||
let restarted = execute_tool(
|
||||
"WorkerRestart",
|
||||
&json!({"worker_id": worker_id}),
|
||||
)
|
||||
.expect("WorkerRestart should succeed");
|
||||
let restarted_output: serde_json::Value = serde_json::from_str(&restarted).expect("json");
|
||||
assert_eq!(
|
||||
restarted_output["status"], "spawning",
|
||||
"restarted worker should be back at spawning"
|
||||
);
|
||||
assert_eq!(restarted_output["trust_gate_cleared"], false,
|
||||
"restart clears trust — next observe loop must re-acquire trust"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn worker_terminate_on_unknown_id_returns_error() {
|
||||
let result = execute_tool(
|
||||
|
||||
Reference in New Issue
Block a user