Sprint 111: IterationSession context primitive Sprint 112: EnvironmentSnapshot and diff Sprint 113: Build output feedback loop Sprint 114: Test failure feedback loop Sprint 115: Requirements derivation from AST Sprint 116: FeedbackLoopOrchestrator (wires 111-115) Sprint 117: HiveMind integration (iteration steps as distributed jobs) These sprints begin Epoch 3 — closed-loop determinism. Sprints 1-110 made code generation open-loop deterministic. Sprints 111+ make the full dev workflow (sense→act→observe→repeat) structurally deterministic, with HiveMind as the execution layer. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
8.6 KiB
Sprint 116 Plan: Feedback Loop Orchestrator
Context
Sprints 111-115 built the primitives: iteration sessions, environment snapshots, build feedback, test feedback, and requirements derivation. Each works independently. Sprint 116 wires them into a single orchestrated loop.
The FeedbackLoopOrchestrator is a state machine that drives an
IterationSession from start to convergence (or escalation). It decides the
next action based on the current session state: if the environment is not
verified, run verify_requirements first; if the build is failing, run
run_build_iteration; if tests are failing, run run_test_iteration. After
each action it selects the highest-confidence fix candidate, applies it, and
loops. When max_attempts is reached without convergence, it escalates to human
with a structured summary of what was tried and what the current gap is.
This is the "inner loop" of deterministic development: goal → sense → act → observe → repeat.
Goals
- Define the orchestrator's decision policy (what to do given current state)
- Implement automatic candidate selection and application
- Handle escalation with a structured "what was tried" summary
- Expose the orchestrator as a single MCP entry point
- Ensure the loop is inspectable at every step (full audit trail)
Steps
Step 1389: OrchestratorPolicy decision model (12 tests)
Create editor/src/OrchestratorPolicy.h.
The policy maps (session_status, gap_model, last_attempt_outcome) → next action type:
- If environment not verified: probe environment
- If requirements gap present: generate setup script → escalate (can't auto-install; human must run the script)
- If build failing: run build iteration, apply top candidate
- If tests failing: run test iteration, apply top candidate
- If gap = 0: mark converged
- If max_attempts reached: escalate
- If last attempt made gap worse (worsening trend, 2+ consecutive): escalate
Each decision includes: action_type, rationale string, confidence float.
Tests (12): environment probe first when no snapshot, requirements gap → escalate, build failure → build iteration, test failure → test iteration, zero gap → converge, max_attempts → escalate, worsening trend → escalate, consecutive no-progress → escalate, decision includes rationale, confidence in range, JSON round-trip of decision, deterministic decision for same inputs.
Step 1390: Candidate auto-selector (10 tests)
Create editor/src/CandidateAutoSelector.h.
Given a list of FixCandidates and a policy, selects the candidate to apply.
Rules:
- Select highest confidence candidate
- Never re-apply a candidate that was already attempted in this session (tracked by candidate description hash)
- If all non-manual candidates were already tried: escalate
- If top candidate is manual (mutation_type=manual): escalate immediately (cannot apply manually)
Tests (10): highest confidence selected, already-tried skipped, all tried → escalate decision, manual type → escalate, empty list → escalate, confidence tie broken deterministically, attempt history consulted, selection logged to session, JSON round-trip of selection record, deterministic output.
Step 1391: Escalation report composer (10 tests)
Create editor/src/EscalationReport.h.
Produces a human-readable + machine-readable summary when the loop escalates:
session_id,goal,goal_typeattempts_made(count)final_gap(GapModel)attempts_summary[]: each attempt's action, outcome, gap_before, gap_aftertop_remaining_candidates[]: the best manual candidates not yet triedescalation_reason(enum: max_attempts, worsening_trend, all_auto_tried, manual_required, infra_gap)recommended_next_step(human-readable string)
Tests (10): report constructable, session_id present, attempts_summary accurate, top candidates included, escalation_reason populated, recommended_next_step human-readable, JSON round-trip, empty attempts handled, gap accurate, deterministic output.
Step 1392: FeedbackLoopOrchestrator core loop (10 tests)
Create editor/src/FeedbackLoopOrchestrator.h.
The orchestrator drives one step of the loop at a time (not a blocking loop — caller steps it). Interface:
step(session_id)→OrchestratorStepResult(action taken, new session status, gap after, escalation_report if escalated)
Internally:
- Load session from SessionStore
- Run OrchestratorPolicy to get next action decision
- Execute the action (build iteration / test iteration / env probe)
- Run CandidateAutoSelector on result
- Apply selected mutation to AST (if non-manual)
- Record attempt, update session, persist
- Return result
Tests (10): step runs without crashing, action matches policy, mutation applied to AST, attempt recorded in session, session converges when gap=0, session escalates at max_attempts, worsening trend triggers escalation, escalation report present when escalated, step result includes gap, deterministic output.
Step 1393: Full loop driver (end-to-end stepping) (10 tests)
Add FeedbackLoopOrchestrator::run_to_completion(session_id, max_wall_s).
Calls step() in a loop until session reaches a terminal state or wall time
expires. Each step result appended to an audit log. Returns the final session
state + full audit log.
Tests (10): run_to_completion returns on convergence, returns on escalation, respects wall time limit, audit log has one entry per step, step count ≤ max_attempts, gap trend visible in audit log, escalation report in last entry if escalated, deterministic for deterministic inputs, session persisted at end, no infinite loop possible.
Step 1394: Audit trail and inspection (8 tests)
Create editor/src/LoopAuditLog.h.
A serializable list of AuditEntry records: one per orchestrator step.
Fields: step_index, action_type, action_description, candidate_applied
(optional), gap_before, gap_after, session_status_after, duration_ms.
Persist to .whetstone/audits/<session_id>.jsonl (newline-delimited JSON,
one entry per line, appendable).
Tests (8): audit entry constructable, appended per step, persisted to JSONL, JSONL format valid, gap_before/after present, candidate_applied present when applied, duration_ms populated, deterministic output.
Step 1395: whetstone_start_feedback_loop MCP tool (8 tests)
Input: { "goal": string, "goal_type": string, "build_command": string?, "test_command": string?, "workspace": string, "max_attempts": int? }
Output: { "session_id": string, "status": "pending" }
Creates the session and registers build/test commands for the orchestrator. Does not start the loop — caller steps it.
Tests (8): tool registered, session_id returned, status is pending, session persisted, build_command stored, test_command stored, missing workspace error, deterministic output.
Step 1396: whetstone_step_feedback_loop MCP tool (8 tests)
Input: { "session_id": string }
Output: { "action_taken": string, "session_status": string, "gap": GapModel, "candidates": FixCandidate[]?, "escalation": EscalationReport? }
Calls orchestrator.step() once and returns the result.
Tests (8): tool registered, action_taken populated, session_status updated, gap present, candidates present on failure, escalation present when escalated, terminal session returns error, deterministic output.
Step 1397: whetstone_run_feedback_loop MCP tool (8 tests)
Input: { "session_id": string, "max_wall_seconds": int? }
Output: { "final_status": string, "steps_taken": int, "final_gap": GapModel, "audit_log": AuditEntry[]?, "escalation": EscalationReport? }
Calls run_to_completion and returns the full result.
Tests (8): tool registered, final_status correct, steps_taken accurate, final_gap present, audit_log included when requested, escalation present when escalated, wall time respected, deterministic output shape.
Step 1398: Sprint 116 integration summary + regression (8 tests)
Create editor/src/Sprint116IntegrationSummary.h.
End-to-end: start_feedback_loop → run_feedback_loop on a project with a known fixable error → verify converged within max_attempts.
Tests (8): headers constructable, tools registered, end-to-end convergence demonstrated, audit log produced, no header exceeds 600 lines, deterministic snapshots, escalation path tested, full prior regression passes.
Architecture Gate
- Orchestrator steps are non-blocking (one step per call) — no background threads, no blocking I/O in the orchestrator itself
- The loop is always inspectable: every step is logged to the audit trail before returning
- Manual candidates never auto-applied — escalation is the only path for mutation_type=manual
- Max 600 lines per header