Skip to content

Commit aee5263

Browse files
committed
test(tools): prove recovery loop against .claw/worker-state.json directly
recovery_loop_state_file_reflects_transitions reads the actual state file after each transition to verify the canonical observability surface reflects the full stall->resolve->ready progression: spawning (state file exists, seconds_since_update present) -> trust_required (is_ready=false, trust_gate_cleared=false in file) -> spawning (trust_gate_cleared=true after WorkerResolveTrust) -> ready_for_prompt (is_ready=true after ready screen observe) This is the end-to-end proof gaebal-gajae called for: clawhip polling .claw/worker-state.json will see truthful state at every step of the recovery loop, including the seconds_since_update staleness signal. 90 tool tests passing, 0 failing.
1 parent 9461522 commit aee5263

1 file changed

Lines changed: 74 additions & 0 deletions

File tree

rust/crates/tools/src/lib.rs

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5656,6 +5656,80 @@ mod tests {
56565656
);
56575657
}
56585658

5659+
#[test]
5660+
fn recovery_loop_state_file_reflects_transitions() {
5661+
// End-to-end proof: .claw/worker-state.json reflects every transition
5662+
// through the stall-detect -> resolve-trust -> ready loop.
5663+
use std::fs;
5664+
5665+
// Use a real temp CWD so state file can be written
5666+
let worktree = temp_path("recovery-loop-state");
5667+
fs::create_dir_all(&worktree).expect("create worktree");
5668+
let cwd = worktree.to_str().expect("utf-8").to_string();
5669+
let state_path = worktree.join(".claw").join("worker-state.json");
5670+
5671+
// 1. Create worker WITHOUT trusted_roots
5672+
let created = execute_tool(
5673+
"WorkerCreate",
5674+
&json!({"cwd": cwd}),
5675+
)
5676+
.expect("WorkerCreate should succeed");
5677+
let created_output: serde_json::Value = serde_json::from_str(&created).expect("json");
5678+
let worker_id = created_output["worker_id"].as_str().expect("worker_id").to_string();
5679+
// State file should exist after create
5680+
assert!(state_path.exists(), "state file should be written after WorkerCreate");
5681+
let state: serde_json::Value = serde_json::from_str(
5682+
&fs::read_to_string(&state_path).expect("read state")
5683+
).expect("parse state");
5684+
assert_eq!(state["status"], "spawning");
5685+
assert_eq!(state["is_ready"], false);
5686+
assert!(state["seconds_since_update"].is_number(), "seconds_since_update must be present");
5687+
5688+
// 2. Force trust_required via observe
5689+
execute_tool(
5690+
"WorkerObserve",
5691+
&json!({"worker_id": worker_id, "screen_text": "Do you trust the files in this folder?"}),
5692+
)
5693+
.expect("WorkerObserve should succeed");
5694+
let state: serde_json::Value = serde_json::from_str(
5695+
&fs::read_to_string(&state_path).expect("read state")
5696+
).expect("parse state");
5697+
assert_eq!(state["status"], "trust_required",
5698+
"state file must reflect trust_required stall");
5699+
assert_eq!(state["is_ready"], false);
5700+
assert_eq!(state["trust_gate_cleared"], false);
5701+
assert!(state["seconds_since_update"].is_number());
5702+
5703+
// 3. WorkerResolveTrust -> state file reflects recovery
5704+
execute_tool(
5705+
"WorkerResolveTrust",
5706+
&json!({"worker_id": worker_id}),
5707+
)
5708+
.expect("WorkerResolveTrust should succeed");
5709+
let state: serde_json::Value = serde_json::from_str(
5710+
&fs::read_to_string(&state_path).expect("read state")
5711+
).expect("parse state");
5712+
assert_eq!(state["status"], "spawning",
5713+
"state file must show spawning after trust resolved");
5714+
assert_eq!(state["trust_gate_cleared"], true);
5715+
5716+
// 4. Observe ready screen -> state file shows ready_for_prompt
5717+
execute_tool(
5718+
"WorkerObserve",
5719+
&json!({"worker_id": worker_id, "screen_text": "Ready for input\n>"}),
5720+
)
5721+
.expect("WorkerObserve ready should succeed");
5722+
let state: serde_json::Value = serde_json::from_str(
5723+
&fs::read_to_string(&state_path).expect("read state")
5724+
).expect("parse state");
5725+
assert_eq!(state["status"], "ready_for_prompt",
5726+
"state file must show ready_for_prompt after ready screen");
5727+
assert_eq!(state["is_ready"], true,
5728+
"is_ready must be true in state file at ready_for_prompt");
5729+
5730+
fs::remove_dir_all(&worktree).ok();
5731+
}
5732+
56595733
#[test]
56605734
fn stall_detect_and_resolve_trust_end_to_end() {
56615735
// 1. Create worker WITHOUT trusted_roots so trust won't auto-resolve

0 commit comments

Comments
 (0)