Skip to content

feat: exit Clone Loop cleanly when Clone signals stop_recommended#11

Merged
de-yi merged 1 commit into
mainfrom
feat/stop-recommended-early-exit
May 12, 2026
Merged

feat: exit Clone Loop cleanly when Clone signals stop_recommended#11
de-yi merged 1 commit into
mainfrom
feat/stop-recommended-early-exit

Conversation

@de-yi
Copy link
Copy Markdown
Contributor

@de-yi de-yi commented May 12, 2026

Summary

Companion PR to cloneisyou/clone#192 — read the server's new `stop_recommended` boolean and let the loop actually exit on satisfaction-shaped predictions instead of force-continuing.

  • `hooks/stop-hook.mjs` — new branch inserted before the existing confidence check: if `prediction.stop_recommended === true` and confidence ≥ threshold, remove the loop state, log a `decision: "satisfied"` history record, print a stderr diagnostic, and return without emitting any `{ decision: "block" }` JSON. Claude's Stop is allowed through, the loop ends.
  • `tests/stop-hook-v2.test.mjs` — two new cases:
    • `exits the loop without blocking when Clone signals stop_recommended` — asserts stdout is empty, state file is removed, history records `satisfied`
    • `ignores stop_recommended when confidence is below threshold` — guards against hallucinated satisfaction at low confidence; falls through to the existing low-confidence escalation
  • `README.md` — updated the "How it works" section to document the new branch alongside the existing above/below-threshold paths.

Decision flow after this PR

confidence stop_recommended result
≥ threshold true Loop exits cleanly (no block; state removed; history satisfied)
≥ threshold false / absent Block + inject prediction as next prompt (existing)
< threshold any Escalate to human (existing)

Why

Concrete repro from upstream daeun:

```
Iteration 3 : good. that's the page.
Confidence: 0.80 / threshold: 0.3
Status: auto
```

Clone correctly predicted satisfaction. The hook still force-continued the loop because the signal was stuck inside the predicted text — the hook just sees a string and a confidence number, and confidence ≥ threshold means "block Claude's Stop". After the server-side PR (cloneisyou/clone#192), Clone now exposes the satisfaction signal as a structured boolean; this PR is what makes that signal actually exit the loop.

Backward-compat

`stop_recommended` is optional. Servers that don't return it (older deploys) leave it `undefined` → never triggers the early-exit branch → identical behavior to today. Plugin upgrades don't have to be synchronized with server upgrades in either direction.

Test plan

  • `npm test` — 25/25, including two new cases for the satisfaction branch
  • After server PR cloneisyou/clone#192 deploys + this PR ships + user upgrades plugin: `/clone:loop "Build a thing. Output COMPLETE when done." --max-iterations 5 --clone-threshold 0.6`. When Clone predicts a satisfaction-shaped reply, loop should exit on that iteration instead of running to max.

🤖 Generated with Claude Code

PR cloneisyou/clone#192 added a `stop_recommended` boolean to the
`predict_next_prompt` tool response. The server sets it when Clone
predicts a satisfaction-shaped reply (e.g. "good. that's the page.",
"ship it") — the user's actual "we're done" voice for an output that
meets their bar.

Read it in the Stop hook and exit the loop cleanly instead of force-
continuing with the prediction as the next prompt. Concretely:

  Above threshold + stop_recommended=true → no `block()` JSON on
    stdout → Claude's Stop is allowed through → loop ends.
  Above threshold + stop_recommended=false → block + inject prediction
    as next prompt (existing behavior).
  Below threshold → escalate to human (existing behavior).

Gated on the same confidence threshold as continuation. A low-
confidence "ship it" is suspicious — could be a hallucinated
satisfaction — so we fall through to the existing low-confidence
escalation path instead of trusting the signal blindly.

State + history:
- Loop state file is removed (loop is genuinely done; new session
  shouldn't try to resume).
- A new history record kind `decision: "satisfied"` lands in
  clone-loop.history.local.jsonl, alongside the existing 'continue'
  and 'escalate-*' kinds. Tooling that tails the history file
  should treat 'satisfied' as a clean stop.

User-facing: a stderr diagnostic line surfaces what just happened so
the user can see why the loop exited at this iteration.

Backward-compat: servers that don't return `stop_recommended` (older
deploys) leave it undefined → never triggers the early-exit branch
→ identical behavior to today.

Tests: two new cases under stop-hook v2 — one for the
satisfaction-exit path, one for the "satisfaction claim ignored when
confidence is below threshold" guard. 25/25 pass.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@de-yi de-yi merged commit 0106623 into main May 12, 2026
@Turtle-Hwan Turtle-Hwan deleted the feat/stop-recommended-early-exit branch May 12, 2026 16:46
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant