Repository navigation
Description
Summary
When the evaluated head is not the commit checked out in the worktree, verify --preview routes to detect against that worktree. Because the worktree does not contain the changed files, detect cannot name the project, returns control_state: human_review_required with next_action.command: null, and the loop ends. Two commands, both authorized by allowed_next_commands, and there is no forward path for either actor.
The remedy — check out the head — is derivable from what preview already knows and is never stated.
Reproduction
google/adk-samples#1745, worktree on main, CLI 0.16.0b7, contract 24.
Step 1
agents-shipgate verify --preview --json --workspace <repo> \
--base 765b4d1fbdcdd397fc48c622c22d757d8c87bafe --head pr-1745
"control": {
"state": "agent_action_required",
"next_action": {
"kind": "discover",
"command": "shipgate detect --workspace <repo> --json",
"why": "The project this change belongs to could not be established (the evaluated head 'pr-1745' is not the commit this worktree has checked out, and project markers are read from the worktree that init would run against) …"
},
"allowed_next_commands": ["shipgate detect --workspace <repo> --json"]
}Step 2 — the only allowed command:
"control_state": "human_review_required",
"next_actor": "human",
"next_action": {"actor": "human", "kind": "review", "command": null},
"verify_required": falseThe diagnosis in step 1 is correct and sharp. The routing that follows from it is not: detect is pointed at a tree that structurally cannot contain python/agents/smart_closer/, because that directory exists only on the PR branch.
Checking out pr-1745 and re-running the identical step-1 command clears the state immediately.
Secondary defect in the same path
detect's escalation hands the human a JSON selector inside a shell command, and no runnable command:
"next_action": "Review: Agents were found in 25 separate projects; this workspace is not one manifest's scope. Run `init --workspace <agent_project_candidates[].path> --write` for the project you are changing.",
"next_actions": [{"kind": "review", "command": null, …}]init already does this correctly — its next_actions[] carry concrete per-candidate commands with executable and args arrays. detect should adopt the same shape.
Why this matters
allowed_next_commands promises a deterministic loop. A loop that terminates in two moves on a legitimate PR — with command: null for the human it escalates to — is worse than no loop, because the contract implied determinism.
Suggested direction
- When
_head_is_current_worktreeis false, say so as the actionable instruction ("check out<head>, or re-run with--headomitted"), rather than routing to a discovery command that reads the wrong tree. - Give
detect's unresolved-scope escalation the same concrete per-candidate commandsinitalready emits.
Related: #327, #369 (structured argv on control contracts).
Found during a first-time-user adoption walk of adk-samples#1745 on 2026-08-20.
Activity
fix(control): make every published recovery reachable and terminating (…
fix(control): match every recovery to the input it was asked for (#397 …
fix(control): ask for the checkout instead of dead-ending the loop (#397