Repository navigation
feat: restore opt-in use_recovery (FailureRecoverySystem port) - #28
Merged
Merged
Conversation
…System First piece of the use_recovery port (see docs/superpowers/specs/2026-09-11-failure-recovery-system-design.md): regex-based error-message classification into ErrorType, ported verbatim from the legacy monolith's IntelligentErrorHandler. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VFjZSRJm926vDGsR6UdUS4
Ports RecoveryAction/RecoveryStrategy and the full 11-error-type RECOVERY_STRATEGIES table plus select_best_strategy's scoring algorithm verbatim from the legacy IntelligentErrorHandler. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VFjZSRJm926vDGsR6UdUS4
…y names TOOL_ALTERNATIVES ports legacy's tool-substitution map, re-keyed from legacy's short names (e.g. "nmap") to current ToolRegistry names (e.g. "nmap_scan"). get_alternative_tool filters every candidate through ToolRegistry.get() so it never suggests an unregistered tool, staying accurate as more categories get migrated. Matches legacy: this only ever suggests, never auto-invokes, the alternative. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VFjZSRJm926vDGsR6UdUS4
adjust_params replaces legacy's regex command-string rewriting (_rebuild_command_with_params, self-documented in the monolith as "a simplified implementation") with a kwargs-dict merge filtered through the target handler's real inspect.signature. An adjustment can never introduce a TypeError: any key the handler doesn't declare is silently skipped rather than applied. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VFjZSRJm926vDGsR6UdUS4
build_escalation ports legacy's escalate_to_human + _get_human_suggestions, dropping the system_resources/previous_errors fields (audit-trail bookkeeping not load-bearing to the recovery decision — see spec non-goals). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VFjZSRJm926vDGsR6UdUS4
Wires classify_error -> select_best_strategy -> (backoff sleep / adjust_params-and-retry / suggest-alternative-and-stop / escalate-and-stop / abort) into the retry loop, capped at max_attempts (default 3, matching legacy). Ports execute_command_with_recovery's structure but calls spec.handler(**kwargs) instead of shelling out a rewritten command string. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VFjZSRJm926vDGsR6UdUS4
POST/GET /api/tools/<name> now accepts use_recovery (default False, a deliberate deviation from the legacy monolith's default-True — see spec §3 for the pentest-safety reasoning). When true, the call runs through hexstrike/core/recovery.py's execute_with_recovery instead of a single direct handler call, restoring the legacy FailureRecoverySystem response contract (recovery_info, alternative_tool_suggested, human_escalation) for callers that rely on it, such as the MCP agent that originally surfaced this as a 400 error before the earlier unknown-params fix made it non-fatal. Also updates test_tool_execution_route_ignores_unknown_params, whose example unsupported field was use_recovery itself — now a recognized flag, not an ignored one — swapped for a genuinely unknown field (legacy_option) so the test still exercises what it was written to test. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VFjZSRJm926vDGsR6UdUS4
…off sleep, and exception classification Addresses six issues found only from the full-branch view after all 7 failure-recovery-system tasks landed individually: GENERIC_ADJUSTMENTS' timeout doubling was applied even when the caller never set timeout (and uncapped), RETRY_WITH_BACKOFF slept uselessly on the loop's terminal attempt, execute_with_recovery never passed the caught exception object into classify_error's type-based shortcuts, the post-loop recovery_applied flag was hardcoded True instead of reflecting actual history, alternative- tool suggestion had no coverage against the real registry, and GET ?use_recovery=true had no affirmative test. Also adds a module docstring and drops an unused `import pytest`. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VFjZSRJm926vDGsR6UdUS4
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
IntelligentErrorHandler/FailureRecoverySysteminto a newhexstrike/core/recovery.py, wired into the tool-execution API behind an opt-inuse_recoveryrequest field (defaultFalse).docs/superpowers/specs/2026-09-11-process-lifecycle-and-task-pool-design.md) not to port this subsystem — reasoning documented indocs/superpowers/specs/2026-09-11-failure-recovery-system-design.md's "Overriding a prior architectural decision" section: not every caller of this HTTP API is an LLM with its own reasoning loop to fall back on.inspect.signatureso it can never introduce aTypeError) instead of legacy's regex command-string rewriting;SWITCH_TO_ALTERNATIVE_TOOLonly suggests (never auto-invokes, matching legacy);GRACEFUL_DEGRADATIONis stubbed asABORT_OPERATION(no fallback-probe logic, explicit non-goal).Process
Built via spec → plan → subagent-driven TDD execution (7 tasks, each independently reviewed) → final whole-branch review → one fix wave → scoped re-review. Docs:
docs/superpowers/specs/2026-09-11-failure-recovery-system-design.mddocs/superpowers/plans/2026-09-11-failure-recovery-system.mdKnown follow-ups (not in this PR, explicitly deferred)
use_recoveryis not yet reachable through the MCP tool bridge (hexstrike/mcp/server.py's strictsig.bindrejects it before it reaches the HTTP API) — direct HTTP/API callers only, for now.use_recoveryfield.timeoutvalue arriving as a GET-query string can raise before int coercion — both pre-existing-adjacent, low-frequency, non-blocking per final review.Test plan
python -m pytest -q --ignore=tests/test_mcp.py— 351 passed, 1 pre-existing unrelated failure (test_browser_agent.py::test_navigate_and_inspect_real_browser_end_to_end, needs a real Chromium binary, fails identically onmasterbefore this branch)tests/test_recovery.pycovering error classification, strategy selection/scoring, alternative-tool suggestion, parameter adjustment, human escalation, and the retry-loop orchestrationtests/test_api.pycoveringuse_recoveryopt-in default, retry-then-succeed, escalation, and both GET query-string truthy/falsy directions🤖 Generated with Claude Code
https://claude.ai/code/session_01VFjZSRJm926vDGsR6UdUS4