mirror of
https://github.com/nearai/ironclaw.git
synced 2026-09-03 08:06:01 +08:00
* feat(engine): execution obligation for v2 — require tool attempt on explicit user commands
When a user explicitly asks the engine to execute something ("run the
tests", "fetch the data", "please check the logs"), the v2 engine now
requires the model to attempt at least one tool/action call before
accepting a plain-text response.
Adds `user_signals_execution_intent()` heuristic in reasoning.rs that
detects imperative execution phrases. The router sets
`require_action_attempt = true` on ThreadConfig when detected. The
Python orchestrator enforces this by nudging the model if it responds
with text-only without attempting any action.
The obligation resolves when the model enters a code/action path
(before execution), preventing retry loops on approval gates. The
obligation nudge and tool-intent nudge are mutually exclusive to
avoid double-nudging.
Closes #2447
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: address review feedback on execution obligation
- Fix nudge interaction bug: move obligation check before
consecutive_nudges reset so tool-intent nudge exhaustion
can't trick the mutual exclusion guard
- Add available-actions guard: obligation only fires when
__get_actions__() returns tools, preventing useless nudges
when no tools are loaded
- Remove "check the " from heuristic: too broad for personal
assistant context ("check the calendar" is a query, not
an execution command)
- Add exhaustion e2e test: model refuses 3 times, hits
max_action_requirement_nudges, text accepted as final
(proves the feature terminates)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* style: cargo fmt
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* style: remove useless .into_iter() to satisfy clippy
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix(engine): enforce execution obligation on follow-up messages
The obligation nudge only fired when spawning a new thread (where
ThreadConfig.require_action_attempt was set). Follow-up messages
injected into a running thread or resuming a suspended thread used
the original thread config, so "run the tests" in turn 2+ was
silently ignored.
Fix: detect execution intent per-message in the Python orchestrator
rather than only from thread config. Two paths covered:
- inject (running thread): check injected message text for intent
keywords, enable obligation and reset state if detected
- resume (suspended thread): check the last user message in the
initial context on run_loop startup
Adds signals_execution_intent() to default.py (ported from Rust
user_signals_execution_intent), plus a multi-turn e2e test that
verifies the inject path: turn 1 is conversational (no obligation),
turn 2 says "run the echo tool" and the nudge fires.
Closes review feedback from henrypark133 on #2539.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* style: fix doc comment placement on strip_code_blocks
The doc comment for strip_code_blocks was incorrectly placed above
user_signals_execution_intent. Moved it to its own function and
cleaned up the user_signals_execution_intent doc.
Addresses gemini review feedback on #2539.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix(engine): reset obligation state on resume + add gate resume test
The context-based obligation check at run_loop startup did not reset
_obligation_resolved and _obligation_nudge_count from persisted state.
On resume, a stale "resolved" flag from a prior run would silently
suppress the new obligation. Fixed by resetting both state flags when
execution intent is detected from context.
Also: the multi-turn e2e test (followup_inject) was mislabeled -- the
test rig processes messages sequentially so turn 2 always spawns a new
thread (the already-working spawn path). Renamed to reflect what it
actually tests.
Added a proper gate-based resume test in engine_v2_gate_integration:
1. Thread spawns with no execution intent in goal
2. Tool call hits a gate, thread enters Waiting
3. Resume with "run the echo tool" (execution intent)
4. Obligation nudge fires, echo tool called
This tests the real resume path through ThreadManager.resume_thread
where ThreadConfig.require_action_attempt was never set.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* style: cargo fmt
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>