v0.3.0 — LLM-driven replanning on step failure
Highlights
Missions can now survive step failures. Configure a Replanner on
your Supervisor and a failing step triggers a recovery prompt instead
of terminating the whole mission. The default missionruntime
LLM-backed replanner calls your existing model adapter with a strict
JSON-output prompt, parses a replacement step sequence, persists it,
and the supervisor continues from there. Up to MaxReplans attempts
per mission (default 3).
This is categorically new behaviour — v0.2.x missions had exactly one
shot at every step. Existing missions without a configured Replanner
keep the v0.2.x fail-fast behaviour unchanged.
Added — LLM-driven replanning on step failure
supervisor.Replanner interface + Supervisor.Replanner / MaxReplans fields
When a step fails in sequential mode, the supervisor consults the
configured Replanner for a replacement step sequence and continues
from there instead of failing the mission. Each mission may be
replanned up to MaxReplans times (default 3).
The Replanner can return ErrReplanRejected to opt out of recovery
for a specific failure (mission fails immediately, no replan attempt
consumed). Any other non-nil error is treated as an implementation
failure and surfaced in the mission.failed reason for diagnostics.
Existing missions without a configured Replanner keep the v0.2.x
fail-fast behaviour — purely additive.
supervisor.NoopReplanner
A Replanner that always returns ErrReplanRejected. Same semantics as
leaving Replanner nil; useful in tests and as documentation.
mission.Manager.Replan(missionID, fromStepID, newSteps, reason)
Persistence-layer replanning. Appends replacement steps after the
failed one, marks every still-unstarted original step that came after
as cancelled (error: "superseded by replan"), rewrites
Mission.PlanJSON to the union, and emits one mission.replanned
event with payload {from_step_id, new_step_count, superseded_step_ids, reason}.
The failed step itself stays in state=failed for the audit log —
replanning never rewrites history. Plans set with
SetPlanParallel keep their parallel=true flag across replans.
missionruntime.NewLLMReplanner(LLMReplannerConfig)
An LLM-backed supervisor.Replanner. Calls a configured
agent.ModelAdapter directly with a strict JSON-output prompt — no
tools, no nested agent loop, so the cost is one Complete call per
attempt and the output schema is enforceable. Retries on malformed
output up to MaxRetries (default 1) with a corrective user turn
appended each retry. Strips common LLM noise (markdown code fences,
leading/trailing prose) before parsing. Empty steps array →
ErrReplanRejected. Per-call timeout Timeout (default 30s).
missionruntime.Options.Replanner / Options.MaxReplans / Runtime.SetReplanner
The runtime passes the configured Replanner to every spawned
Supervisor. SetReplanner mirrors SetExecutor for hot-swap at app
startup once the model adapter is resolved.
Scope notes
- Sequential mode only. Parallel dispatch (
SetPlanParallel)
still fails the whole mission on the first step failure; in-flight
peers complete naturally but no replan attempt is made. Parallel-
mode replanning has its own design problem (what to do with
in-flight peers whose results are partway done) and is a separate
follow-up. - Bounded scope. The Replanner sees the goal, completed step
outputs, the failed step + error, and the remaining unstarted steps.
It does NOT see the mission's running event log or per-step
telemetry. A richer audit-trail integration is a clearly-scoped
follow-up.
Tests
10 new tests across three layers:
internal/agent/supervisor(5 tests) — recovery via replacement
steps,ErrReplanRejectedshort-circuit,MaxReplanscap
enforcement,nilReplanner preserves old fail-fast behavior,
NoopReplannerbehaves like no Replanner.internal/mission(4 tests) — full happy-path persistence contract
including event payload + supersede semantics, state guard,
ID validation, empty-input validation.internal/agent/missionruntime(6 tests) — clean JSON happy path,
empty-steps →ErrReplanRejected, malformed-then-recovers retry
flow, markdown-fenced parses anyway, retries-exhausted error,
model error propagation.
Full repo go test -race -count=1 ./... passes across all 127
packages.
Compatibility
No breaking changes. The new fields on Supervisor and
missionruntime.Options are optional; zero values preserve v0.2.x
behaviour exactly. No mission schema, plan, or wire-format change.
Full changelog: v0.2.3...v0.3.0