The self-correction loop

Shipmoor Team
June 11, 2026
3 min read

In release — preview. This surface is fully built and in the release process; it is not yet generally available. Treat the commands and behavior here as preview until GA.

The point of the harness is one closed loop: the agent edits, the edit is scanned, a finding goes straight back to the agent, and the agent fixes its own mistake before you ever see it. This page walks that loop on Claude Code — the harness’s primary agent — using output from a real session against shipmoor 0.4.0.

The wiring

shipmoor-harness install claude injects two hooks into .claude/settings.json’s top-level hooks key, beside any hooks you already have:

  • PostToolUse (matching Edit|MultiEdit|Write|NotebookEdit) — scans after every edit.
  • Stop — a final pass when the agent finishes.

Claude Code invokes the handler with the tool payload on stdin:

shipmoor-harness hook claude-code post-tool-use
# stdin: {"hook_event_name":"PostToolUse","tool_name":"Edit","tool_input":{"file_path":"pay.py"}}

The handler finds the changed file from the payload (falling back to scan --changed), scans it with the real CLI, and answers in the project’s configured mode.

Turn 1 — block

The agent edits pay.py and hallucinates a dependency: import receipt_engine. The hook scans the edit, finds the phantom import, and stops the tool — exit code 2, findings on stderr, which Claude Code feeds back to the model:

[shipmoor-harness] Shipmoor found 1 issue (1 high).

[high] pay.py:6 python.phantom_import
  Local module 'receipt_engine' is referenced but no file matches under PYTHONPATH.
  → 'receipt_engine' looks local but does not resolve from project module paths.
    Add the file, fix PYTHONPATH, or remove the import.

Turn 2 — self-correction

The agent, seeing exactly which line and why, removes the import. The hook re-runs on the corrected edit and clears: exit 0, no output. The loop closed without a human in it.

A clean change is always silent — the harness never adds noise to a session that doesn’t need it. And if the CLI is missing or errors, the hook degrades to a no-op rather than breaking your agent.

Feedback mode — advise, don’t stop

Under mode feedback, the same finding never blocks. The handler exits 0 and returns the finding in Claude Code’s documented hook-response shape, landing in the model’s context as additional information:

{"hookSpecificOutput": {"hookEventName": "PostToolUse",
  "additionalContext": "Shipmoor found 1 issue (1 high).\n\n[high] pay.py:6 python.phantom_import\n  Local module 'receipt_engine' is referenced but no file matches under PYTHONPATH.\n  → 'receipt_engine' looks local but does not resolve from project module paths. Add the file, fix PYTHONPATH, or remove the import."}}

The feedback text is deterministic — severity-ordered, path:line, rule ID, recommendation — formatted from the parsed JSON report, never from scraped human output.

The loop is bounded

harness.max_feedback_cycles (default 3) caps how many times findings are routed back within one loop. When the cap is reached, whatever remains is surfaced instead of re-prompted — the agent can never ping-pong forever against a finding it can’t fix.

Try it yourself

Dry-run the handler with a synthetic payload before relying on it:

printf '{"tool_name":"Edit","tool_input":{"file_path":"path/to/edited.py"}}' \
  | shipmoor-harness hook claude-code post-tool-use

Next

Last updated on June 11, 2026

Was this article helpful?

Your response is saved on this device.