diff --git a/sprint111_plan.md b/sprint111_plan.md new file mode 100644 index 0000000..56d47cb --- /dev/null +++ b/sprint111_plan.md @@ -0,0 +1,175 @@ +# Sprint 111 Plan: Iteration Session Context + +## Context + +Sprints 1-110 established Whetstone as a deterministic *open-loop* generator: +schema → struct, spec → taskitems, source → transpiled output. The output is +fully determined by the input, with no environmental state involved. + +Sprint 111 begins a new capability tier: **closed-loop iteration**. Real +development is not open-loop. It involves running code, observing what breaks, +forming a hypothesis, patching, and repeating until a goal is met. This sprint +introduces the foundational primitive for that loop: the `IterationSession`. + +An `IterationSession` is a structured record of one attempt to achieve a +convergence goal (all tests pass, build succeeds, environment is ready). It +tracks: the goal, each attempt taken, each observation made, and the current +distance from the goal. Every subsequent sprint in this epoch builds on it. + +--- + +## Goals + +1. Define the `IterationSession` schema with goal, attempt history, and state +2. Define `AttemptRecord` and `ObservationRecord` as structured sub-types +3. Define `GapModel` to measure distance from the convergence goal +4. Implement a state machine: pending → active → converged / escalated / failed +5. Persist sessions to sidecar files alongside annotated ASTs + +--- + +## Steps + +### Step 1339: `IterationSession` core schema (12 tests) + +Create `editor/src/IterationSession.h`. + +Fields: `session_id`, `goal` (string), `goal_type` (enum: build_success, +tests_pass, environment_ready, custom), `status` (enum: pending, active, +converged, escalated, failed), `created_at`, `updated_at`, `attempts[]`, +`max_attempts` (default 10). + +Tests (12): constructable with defaults, goal stored, goal_type round-trips, +status defaults to pending, session_id is non-empty on construction, +attempt list starts empty, max_attempts enforced, JSON serialization, +JSON deserialization, unknown status survives passthrough, +deterministic serialization order, schema version field present. + +### Step 1340: `AttemptRecord` schema (10 tests) + +Create `editor/src/AttemptRecord.h`. + +Fields: `attempt_id`, `session_id`, `action_type` (enum: build, test, patch, +probe, escalate), `action_description`, `started_at`, `completed_at`, +`outcome` (enum: success, failure, partial, error), `observation_id`. + +Tests (10): constructable, action_type round-trips, outcome defaults to error, +duration computable from timestamps, JSON round-trip, unknown action_type +passthrough, attempt_id non-empty, links to session_id, links to observation_id, +deterministic serialization. + +### Step 1341: `ObservationRecord` schema (10 tests) + +Create `editor/src/ObservationRecord.h`. + +Fields: `observation_id`, `source` (enum: build_output, test_output, +process_stdout, process_stderr, file_read, env_probe), `raw_text`, +`exit_code`, `duration_ms`, `structured_data` (JSON blob for parsed output), +`truncated` (bool — raw_text may be capped at 16KB). + +Tests (10): constructable, source enum round-trips, exit_code defaults to -1, +truncated flag set when raw_text exceeds limit, structured_data round-trips, +JSON serialization, deserialization, unknown source passthrough, +observation_id non-empty, deterministic output. + +### Step 1342: `GapModel` — distance from convergence goal (10 tests) + +Create `editor/src/GapModel.h`. + +Fields: `metric_type` (enum: error_count, test_failure_count, missing_deps, +custom), `current_value`, `target_value`, `delta`, `converged` (bool — +true when current_value == target_value), `trend` (enum: improving, +stagnant, worsening — derived from last two values). + +Tests (10): converged true when delta is zero, trend improving when delta +decreasing, trend worsening when delta increasing, stagnant after two equal +values, JSON round-trip, negative delta flagged as worsening, custom metric +type passthrough, deterministic output, delta auto-computed, schema constructable. + +### Step 1343: Session state machine transitions (10 tests) + +Add `SessionStateMachine` to `IterationSession.h` (or small companion header). + +Valid transitions: +- pending → active (on first attempt recorded) +- active → converged (on gap.converged == true) +- active → escalated (on max_attempts reached without convergence) +- active → failed (on attempt with outcome=error and no recovery path) +- Any terminal state is immutable. + +Tests (10): pending→active on first attempt, active→converged when gap closes, +active→escalated at max_attempts, active→failed on terminal error, +illegal transition rejected, terminal states immutable, state serialized, +state deserialized, history preserved through transitions, deterministic +transition log. + +### Step 1344: Session persistence to sidecar file (10 tests) + +Add `SessionStore` to a new `editor/src/SessionStore.h`. + +Sidecar path convention: `.whetstone/sessions/.json` relative +to workspace root. `SessionStore::save(session)` writes atomically (write +to `.tmp` then rename). `SessionStore::load(session_id)` reads and +deserializes. `SessionStore::list()` returns all session IDs in the store. + +Tests (10): save creates file, load round-trips session, list returns saved ids, +overwrite preserves new state, missing session returns nullopt, atomic write +(partial write does not corrupt), path derivation from workspace root, +concurrent save does not corrupt (file lock), deterministic file layout, +sidecar dir created on first save. + +### Step 1345: `whetstone_start_iteration_session` MCP tool (8 tests) + +Create `editor/src/mcp/RegisterIterationSessionTools.h`. + +Input: `{ "goal": string, "goal_type": string, "max_attempts": int? }` +Output: `{ "session_id": string, "status": "pending" }` + +Persists session to SessionStore immediately on creation. + +Tests (8): tool registered, schema validates, session_id returned, +status is pending, session persisted to disk, missing goal_type defaults +to "custom", max_attempts defaults to 10, deterministic output shape. + +### Step 1346: `whetstone_record_attempt` MCP tool (8 tests) + +Input: `{ "session_id": string, "action_type": string, + "action_description": string, "observation": { ... } }` +Output: `{ "attempt_id": string, "session_status": string, "gap": { ... }? }` + +Appends AttemptRecord + ObservationRecord to session. Returns updated +session status after state machine step. + +Tests (8): tool registered, attempt appended, session_status updated, +unknown session_id returns error, illegal transition returns error, +gap field present when gap model updated, deterministic output, +session persisted after record. + +### Step 1347: `whetstone_get_session_state` MCP tool (8 tests) + +Input: `{ "session_id": string }` +Output: full session JSON with attempts[], gap, and current status. + +Tests (8): tool registered, full session returned, attempts included, +gap included when present, terminal sessions readable, missing session +returns structured error, deterministic serialization, attempt count correct. + +### Step 1348: Sprint 111 integration summary + regression (8 tests) + +Create `editor/src/Sprint111IntegrationSummary.h`. + +Tests (8): all new headers constructable, tools registered in initialize +response, session round-trip end-to-end (start → record × 2 → converge → +read), state machine covers all transitions, sidecar created on disk, +no header exceeds 600 lines, deterministic snapshots, full prior regression +passes. + +--- + +## Architecture Gate + +- Max 600 lines per header — split `IterationSessionExtended.h` if needed +- No runtime dependencies beyond nlohmann-json and std::filesystem +- Session IDs use UUIDv4 (reuse existing UUID utility if present, else add one) +- All MCP tool outputs are stable and machine-readable +- Sidecar format is versioned (`"schema_version": 1`) diff --git a/sprint112_plan.md b/sprint112_plan.md new file mode 100644 index 0000000..9d56070 --- /dev/null +++ b/sprint112_plan.md @@ -0,0 +1,168 @@ +# Sprint 112 Plan: Environment Snapshot and Diff + +## Context + +Before iterating on a failing environment, you must read it deterministically. +"Environment" means the intersection of: installed packages, active config files, +running processes, and file system state that your project depends on. Without a +structured snapshot, the iteration session has no baseline to compare against and +no way to tell whether a change actually helped. + +Sprint 112 adds `EnvironmentSnapshot` — a structured, serializable record of the +environment at a point in time — and `EnvironmentDiff` — the delta between two +snapshots. These are the observability primitives that the build, test, and setup +feedback loops in later sprints will use. + +--- + +## Goals + +1. Define `EnvironmentSnapshot` schema covering deps, configs, processes, and paths +2. Implement probes that read each domain from the actual system +3. Define `EnvironmentDiff` as a typed delta between two snapshots +4. Expose snapshot and diff as MCP tools +5. Produce a setup verification report (what is present vs what is needed) + +--- + +## Steps + +### Step 1349: `EnvironmentSnapshot` core schema (12 tests) + +Create `editor/src/EnvironmentSnapshot.h`. + +Fields: `snapshot_id`, `captured_at`, `workspace_root`, `platform` +(os, arch, shell), `packages[]` (name, version, source: apt/pip/npm/cargo/etc.), +`config_files[]` (path, hash, key_values[] for known formats), +`processes[]` (name, pid, port, status), `env_vars[]` (key, value — filtered +to relevant set, never secrets). + +Tests (12): constructable with defaults, snapshot_id non-empty, platform fields +populated, packages list starts empty, config list starts empty, process list +starts empty, env_vars list starts empty, JSON serialization, JSON deserialization, +unknown package source passthrough, schema version present, deterministic output. + +### Step 1350: Package state probe (10 tests) + +Create `editor/src/PackageProbe.h`. + +Probes installed packages from available managers on the current platform. +Strategy: try each manager (dpkg-query, pip list, npm list -g, cargo --list) +via subprocess, parse stdout into PackageRecord rows, tag with source. +Failures are non-fatal — missing manager produces empty list for that source. + +Tests (10): probe runs without crashing, result is parseable, known-installed +package appears in results, unknown package absent, source tag correct, +failed probe returns empty not error, deterministic sort order, version field +populated, JSON round-trip, timeout respected (default 5s). + +### Step 1351: Config file probe (10 tests) + +Create `editor/src/ConfigFileProbe.h`. + +Scans workspace for known config files: `.env`, `*.yaml`, `*.toml`, `*.json` +(at workspace root depth only — no deep recursion). Reads content, computes +SHA-256 hash, extracts key_values for .env and simple YAML/TOML maps. +Sensitive keys (PASSWORD, SECRET, KEY, TOKEN) are redacted to `"[REDACTED]"`. + +Tests (10): .env file parsed into key_values, sensitive keys redacted, +hash computed and stable, yaml keys extracted, toml keys extracted, +json keys extracted (top-level only), missing file produces absent entry, +hash changes on content change, deterministic key ordering, depth limit +respected. + +### Step 1352: Process state probe (10 tests) + +Create `editor/src/ProcessProbe.h`. + +Lists running processes filtered to a known-relevant set (configurable list of +process name substrings). Uses `/proc` on Linux, `ps` subprocess on macOS. +Captures name, pid, and listening ports (from `ss -tlnp` or `netstat`). + +Tests (10): probe runs without crashing, result parseable, known process +appears when running, absent process not in list, port captured when listening, +no port when not listening, deterministic sort, name filter respected, +JSON round-trip, timeout respected. + +### Step 1353: `EnvironmentDiff` schema and computation (10 tests) + +Create `editor/src/EnvironmentDiff.h`. + +Compares two `EnvironmentSnapshot` instances. Fields: `added_packages[]`, +`removed_packages[]`, `changed_packages[]` (name + old_version + new_version), +`added_configs[]`, `removed_configs[]`, `changed_configs[]` (path + old_hash ++ new_hash), `added_processes[]`, `removed_processes[]`, `unchanged` (bool — +true when all diff lists are empty). + +Tests (10): empty diff on identical snapshots, unchanged true on identical, +added package detected, removed package detected, changed package detected, +added config detected, changed config hash detected, process added detected, +JSON round-trip, deterministic diff ordering. + +### Step 1354: Snapshot persistence (sidecar integration) (8 tests) + +Extend `SessionStore.h` (or add `SnapshotStore.h`) to persist +`EnvironmentSnapshot` alongside iteration sessions. + +Convention: `.whetstone/snapshots/.json` + +Tests (8): save creates file, load round-trips, list returns ids, overwrite +preserves new data, missing snapshot returns nullopt, atomic write, path +derivation correct, sidecar dir created on first save. + +### Step 1355: Setup verification report (8 tests) + +Create `editor/src/SetupVerificationReport.h`. + +Given a `RequiredEnvironment` spec (a list of expected packages, config files, +and processes) and a current `EnvironmentSnapshot`, produces a structured report: +`satisfied[]`, `missing[]`, `version_mismatch[]`, `ready` (bool — true when +missing and version_mismatch are both empty). + +Tests (8): ready true when all present, missing package detected, version +mismatch detected, correct version passes, empty required produces ready=true, +JSON round-trip, deterministic output, report serializable. + +### Step 1356: `whetstone_snapshot_environment` MCP tool (8 tests) + +Input: `{ "workspace": string, "process_filter": string[]? }` +Output: full `EnvironmentSnapshot` JSON + `snapshot_id`. + +Runs all three probes (packages, configs, processes) and persists result. + +Tests (8): tool registered, snapshot_id returned, packages field present, +configs field present, processes field present, snapshot persisted to disk, +invalid workspace returns structured error, deterministic output shape. + +### Step 1357: `whetstone_diff_environments` MCP tool (8 tests) + +Input: `{ "before_id": string, "after_id": string }` +Output: `EnvironmentDiff` JSON + `unchanged` bool. + +Loads both snapshots from store, computes diff, returns result. + +Tests (8): tool registered, unchanged true on same snapshot, added package +appears in diff, removed package appears, changed package appears, missing +snapshot_id returns error, diff persisted to session if session_id provided, +deterministic output. + +### Step 1358: Sprint 112 integration summary + regression (8 tests) + +Create `editor/src/Sprint112IntegrationSummary.h`. + +End-to-end test: capture snapshot → mutate environment (add a dummy config +file) → capture second snapshot → diff → verify change detected. + +Tests (8): headers constructable, tools registered, end-to-end diff detects +change, verification report correct, sidecar files created, no header exceeds +600 lines, deterministic snapshots, full prior regression passes. + +--- + +## Architecture Gate + +- Probes are non-fatal: subprocess failure returns empty list, not panic +- No new dependencies — subprocess via std::system or popen, parsing in C++ +- Sensitive data never stored in snapshots (redaction in ConfigFileProbe) +- Max 600 lines per header +- All MCP outputs stable and machine-readable diff --git a/sprint113_plan.md b/sprint113_plan.md new file mode 100644 index 0000000..318554f --- /dev/null +++ b/sprint113_plan.md @@ -0,0 +1,196 @@ +# Sprint 113 Plan: Build Output Feedback Loop + +## Context + +Build failures are the most common reason a development iteration stalls. +They are also highly structured: compilers emit errors in machine-parseable +formats (file, line, col, error code, message). That structure is currently +discarded — the developer reads the raw output and decides what to fix. + +Sprint 113 makes build output a first-class Whetstone input. A build failure +becomes a structured `BuildErrorRecord`, which is located in the AST to +identify the responsible node, classified by error type, and used to generate +a ranked list of fix candidates. Each candidate is a concrete AST mutation +with a confidence score. The result integrates with the IterationSession from +Sprint 111 — each build attempt becomes a recorded attempt with a +structured observation. + +--- + +## Goals + +1. Parse compiler output (GCC, Clang, CMake) into structured `BuildErrorRecord`s +2. Locate each error in the AST by file + line + col +3. Classify error types (missing dep, type error, undefined symbol, etc.) +4. Generate ranked fix candidates as AST mutations +5. Integrate with IterationSession (build attempt → AttemptRecord) + +--- + +## Steps + +### Step 1359: `BuildErrorRecord` schema (12 tests) + +Create `editor/src/BuildErrorRecord.h`. + +Fields: `error_id`, `tool` (enum: gcc, clang, clang_tidy, cmake, linker, +unknown), `severity` (enum: error, warning, note), `file_path`, `line`, +`col`, `error_code` (e.g. "E0137", "-Wunused"), `message`, `context_lines[]` +(surrounding source lines captured from file), `ast_node_id` (populated by +locator, initially empty). + +Tests (12): constructable with defaults, tool enum round-trips, severity +defaults to error, file_path stored, line/col stored, error_code stored, +message stored, context_lines starts empty, ast_node_id starts empty, +JSON serialization, JSON deserialization, deterministic output. + +### Step 1360: Build output parser — GCC/Clang format (10 tests) + +Create `editor/src/BuildOutputParser.h`. + +Parses the stdout/stderr of a build command into `BuildErrorRecord` list. +GCC/Clang format: `::: : `. +CMake format: `CMake Error at :`. +Linker format: `undefined reference to ''`. +Unknown lines captured as `tool=unknown` with raw message. + +Tests (10): GCC error parsed correctly, Clang error parsed, CMake error +parsed, linker error parsed, warning parsed with severity=warning, note +parsed with severity=note, multi-line error grouped, empty input returns +empty list, malformed line returns unknown record, deterministic parse order. + +### Step 1361: AST node locator from build error (10 tests) + +Create `editor/src/BuildErrorLocator.h`. + +Given a `BuildErrorRecord` (file_path, line, col) and an open AST, finds +the AST node responsible. Strategy: find the node whose span contains +(line, col). If exact match fails, find the nearest enclosing function/class. +Populates `ast_node_id` on the record. + +Tests (10): exact line/col match returns node, enclosing function found when +exact fails, file mismatch returns nullopt, line 0 returns nullopt, col 0 +accepted (start of line), multiple errors located in one pass, deterministic +node selection, linker error (no file) returns nullopt gracefully, node_id +written to record, works across all supported languages. + +### Step 1362: Build error classifier (10 tests) + +Create `editor/src/BuildErrorClassifier.h`. + +Classifies `BuildErrorRecord` into semantic error types: +- `missing_include` — #include not found +- `undefined_symbol` — undeclared identifier or linker undefined ref +- `type_mismatch` — incompatible types in assignment/call +- `missing_dependency` — cmake target/package not found +- `syntax_error` — parse-level failure +- `resource_limit` — OOM, stack overflow +- `unknown` — catch-all + +Classification uses error_code patterns + message substring matching. + +Tests (10): missing_include classified, undefined_symbol classified, +type_mismatch classified, cmake dependency failure classified, syntax_error +classified, linker undefined ref → undefined_symbol, warning not +misclassified as error type, unknown catch-all, deterministic classification, +JSON round-trip of classified record. + +### Step 1363: Fix candidate generator (10 tests) + +Create `editor/src/BuildFixCandidateGenerator.h`. + +Given a classified `BuildErrorRecord` and the open AST, generates a ranked +list of `FixCandidate` objects. Each candidate has: `candidate_id`, +`description`, `confidence` (0.0–1.0), `mutation_type` (addImport, +changeType, addDependency, deleteNode, insertNode, manual), `mutation` +(JSON blob suitable for passing to `whetstone_mutate`). + +Classification → candidate strategy: +- `missing_include` → addImport candidate for the likely header +- `undefined_symbol` → suggest declaration or import fix +- `type_mismatch` → suggest cast or type change +- `missing_dependency` → suggest vcpkg/apt install command (manual mutation) +- `syntax_error` → low-confidence deleteNode or manual +- `unknown` → manual with description only + +Tests (10): missing_include produces addImport candidate, confidence > 0.5 +for known patterns, confidence < 0.3 for unknown, mutation JSON round-trips, +manual candidate always produced as fallback, candidates ordered by confidence +desc, candidate_id non-empty, description human-readable, no candidates on +empty error list, deterministic output. + +### Step 1364: Build iteration session integration (8 tests) + +Create `editor/src/BuildIterationAdapter.h`. + +Wraps a build attempt as an `AttemptRecord` + `ObservationRecord`: +- Runs build command via subprocess, captures stdout+stderr+exit_code +- Parses output into `BuildErrorRecord` list +- Computes `GapModel` (metric_type=error_count, current=errors.size(), + target=0) +- Records attempt in provided `IterationSession` +- Returns list of `FixCandidate` for all errors + +Tests (8): attempt appended to session, observation raw_text captured, +gap metric_type is error_count, gap converged when build succeeds, +session status transitions to converged on success, fix candidates returned, +attempt outcome=success on exit_code 0, attempt outcome=failure on non-zero. + +### Step 1365: `whetstone_parse_build_output` MCP tool (8 tests) + +Input: `{ "output": string, "tool": string? }` +Output: `{ "errors": BuildErrorRecord[], "warning_count": int, "error_count": int }` + +Parses raw build output string, classifies each record, returns structured list. + +Tests (8): tool registered, error_count correct, warning_count correct, +tool field respected, empty output returns empty errors, malformed input +returns structured error, classification applied to all records, +deterministic output shape. + +### Step 1366: `whetstone_suggest_build_fix` MCP tool (8 tests) + +Input: `{ "session_id": string?, "build_output": string, + "file": string?, "line": int?, "col": int? }` +Output: `{ "candidates": FixCandidate[], "error_count": int }` + +Parses output, locates in active AST buffer, classifies, generates candidates. + +Tests (8): tool registered, candidates returned, confidence present on all, +mutation field present, manual fallback always present, missing AST graceful, +candidates sorted by confidence, deterministic output. + +### Step 1367: `whetstone_run_build_iteration` MCP tool (8 tests) + +Input: `{ "session_id": string, "build_command": string, + "workspace": string }` +Output: `{ "attempt_id": string, "session_status": string, + "error_count": int, "candidates": FixCandidate[] }` + +Runs the build, records the attempt in the session, returns fix candidates. + +Tests (8): tool registered, attempt_id returned, session_status updated, +error_count correct, candidates present on failure, session converged on +success, build timeout enforced (default 120s), deterministic output shape. + +### Step 1368: Sprint 113 integration summary + regression (8 tests) + +Create `editor/src/Sprint113IntegrationSummary.h`. + +End-to-end: start session → run_build_iteration (failing build) → verify +attempt recorded + candidates returned → apply top candidate → run again → +verify error count reduced. + +Tests (8): headers constructable, tools registered, end-to-end flow works, +gap model improves after fix applied, session transitions correctly, no header +exceeds 600 lines, deterministic snapshots, full prior regression passes. + +--- + +## Architecture Gate + +- Build command execution via popen/std::system — not a shell injection vector + (command is passed as-is from MCP caller; caller must sanitize inputs) +- Confidence scores are heuristic — never presented as guarantees +- Manual fallback candidate always generated so the loop is never stuck +- Max 600 lines per header diff --git a/sprint114_plan.md b/sprint114_plan.md new file mode 100644 index 0000000..5ef6874 --- /dev/null +++ b/sprint114_plan.md @@ -0,0 +1,186 @@ +# Sprint 114 Plan: Test Failure Feedback Loop + +## Context + +Build success is only the first gate. Tests are the second gate, and they carry +richer semantic signal than compiler errors: a failing assertion tells you not +just *where* something went wrong but *what* the code was supposed to do versus +what it actually did. That gap between expected and actual is exactly the kind of +structured signal Whetstone can act on deterministically. + +Sprint 114 adds test output parsing, failure classification, test-to-source +location mapping, and fix candidate generation for test failures. It mirrors +the architecture of Sprint 113 (Build Feedback Loop) but operates on test +output formats (Google Test, pytest, Jest, TAP). The result integrates with +IterationSession: each test run becomes an AttemptRecord with structured +observations and a GapModel keyed on failing test count. + +--- + +## Goals + +1. Parse test runner output (gtest, pytest, Jest, TAP) into `TestFailureRecord`s +2. Classify failures by type: assertion, exception, timeout, infra, wrong output +3. Map each failure to a source location and AST node +4. Generate ranked fix candidates from test failures +5. Integrate with IterationSession (test run → AttemptRecord) + +--- + +## Steps + +### Step 1369: `TestFailureRecord` schema (12 tests) + +Create `editor/src/TestFailureRecord.h`. + +Fields: `failure_id`, `runner` (enum: gtest, pytest, jest, tap, unknown), +`test_suite`, `test_name`, `failure_type` (enum: assertion, exception, +timeout, missing_output, wrong_output, infra, unknown), +`expected_value` (string, may be empty), `actual_value` (string, may be empty), +`file_path`, `line`, `message`, `ast_node_id` (populated by locator). + +Tests (12): constructable, runner enum round-trips, failure_type defaults to +unknown, expected/actual stored, file_path stored, line stored, message stored, +ast_node_id starts empty, JSON serialization, JSON deserialization, unknown +runner passthrough, deterministic output. + +### Step 1370: Test output parser — gtest format (10 tests) + +Create `editor/src/TestOutputParser.h`. + +Parses Google Test stdout format: +- `[ FAILED ] SuiteName.TestName (Nms)` → failure header +- `Expected: ` / ` Actual: ` → assertion fields +- `:: Failure` → source location + +Also parses pytest (`FAILED test_file.py::test_name - AssertionError: ...`) +and basic TAP (`not ok N - description`). Jest support for `✕ test name` +format. Unknown lines attached to current test as raw message. + +Tests (10): gtest failure parsed, gtest assertion expected/actual extracted, +gtest file/line extracted, pytest failure parsed, jest failure parsed, +TAP not-ok parsed, pass lines ignored, multi-failure batch parsed, +empty input returns empty list, deterministic parse order. + +### Step 1371: Test failure classifier (10 tests) + +Create `editor/src/TestFailureClassifier.h`. + +Classifies `TestFailureRecord` into semantic types using message patterns: +- `assertion` — Expect/Assert macro failure (expected != actual) +- `exception` — unhandled exception or signal in test +- `timeout` — test exceeded time limit +- `wrong_output` — stdout/output mismatch (snapshot tests) +- `missing_output` — expected output file not produced +- `infra` — test setup/teardown failure, missing fixture +- `unknown` — catch-all + +Tests (10): assertion classified, unhandled exception classified, timeout +classified, snapshot mismatch → wrong_output, missing file → missing_output, +fixture failure → infra, unknown catch-all, deterministic classification, +classification applied to batch, JSON round-trip of classified record. + +### Step 1372: Test-to-source locator (10 tests) + +Create `editor/src/TestSourceLocator.h`. + +Maps a `TestFailureRecord` to the implementation code under test, not just +the test file. Strategy: +1. If file_path is a test file (matches `*test*`, `*spec*`), find the + corresponding implementation file by naming convention. +2. Locate the function-under-test by test_name substring match against + AST function names. +3. Fall back to the test file's AST node if implementation not found. + +Populates `ast_node_id` on the failure record. + +Tests (10): test file maps to implementation file, function-under-test found +by name, fallback to test file node, no match returns nullopt gracefully, +gtest suite.test → function match, pytest test_name → function match, +deterministic selection, works across Python and C++, batch locating works, +ast_node_id written to record. + +### Step 1373: Fix candidate generator for test failures (10 tests) + +Create `editor/src/TestFixCandidateGenerator.h`. + +Generates `FixCandidate` list from classified `TestFailureRecord`: + +- `assertion` (expected != actual): suggest value correction or logic change + at the implementation AST node. Confidence = 0.4–0.7 (semantic, heuristic). +- `exception`: suggest try/catch addition or precondition guard. Confidence 0.3. +- `timeout`: suggest async/await addition or algorithm complexity reduction hint. + Confidence 0.2 (manual candidate prominent). +- `wrong_output` / `missing_output`: suggest regenerating snapshot or checking + file write path. Manual candidate. +- `infra`: suggest fixture setup. Manual. +- `unknown`: manual only. + +Tests (10): assertion produces value-correction candidate, confidence in range, +exception produces guard candidate, timeout produces manual prominent, +wrong_output produces snapshot candidate, batch processing works, candidates +ordered by confidence, manual fallback always present, deterministic output, +candidate mutations JSON round-trip. + +### Step 1374: Test iteration session integration (8 tests) + +Create `editor/src/TestIterationAdapter.h`. + +Mirrors `BuildIterationAdapter`. Runs test command via subprocess, parses +output, computes GapModel (metric_type=test_failure_count), records attempt +in IterationSession, returns FixCandidates. + +Tests (8): attempt appended to session, observation raw_text captured, +gap metric_type is test_failure_count, gap converged when all tests pass, +session converges on zero failures, fix candidates returned on failures, +attempt outcome=success on zero failures, attempt outcome=failure on any failure. + +### Step 1375: `whetstone_parse_test_output` MCP tool (8 tests) + +Input: `{ "output": string, "runner": string? }` +Output: `{ "failures": TestFailureRecord[], "pass_count": int, "fail_count": int }` + +Tests (8): tool registered, fail_count correct, pass_count correct, runner +field respected, empty output returns empty failures, malformed input handled, +classification applied, deterministic output. + +### Step 1376: `whetstone_suggest_test_fix` MCP tool (8 tests) + +Input: `{ "session_id": string?, "test_output": string, "runner": string? }` +Output: `{ "candidates": FixCandidate[], "fail_count": int }` + +Tests (8): tool registered, candidates returned, confidence present, +mutation field present, manual fallback always present, missing AST graceful, +candidates sorted by confidence, deterministic output. + +### Step 1377: `whetstone_run_test_iteration` MCP tool (8 tests) + +Input: `{ "session_id": string, "test_command": string, "workspace": string }` +Output: `{ "attempt_id": string, "session_status": string, + "fail_count": int, "candidates": FixCandidate[] }` + +Tests (8): tool registered, attempt_id returned, session_status updated, +fail_count correct, candidates on failure, session converged on all-pass, +timeout enforced (default 120s), deterministic output shape. + +### Step 1378: Sprint 114 integration summary + regression (8 tests) + +Create `editor/src/Sprint114IntegrationSummary.h`. + +End-to-end: start session → run_test_iteration (failing tests) → verify +attempt recorded + candidates returned → apply top candidate → run again → +verify fail count reduced. + +Tests (8): headers constructable, tools registered, end-to-end flow works, +gap model improves after fix, session transitions correctly, no header exceeds +600 lines, deterministic snapshots, full prior regression passes. + +--- + +## Architecture Gate + +- Assertion parsing is heuristic — expected/actual extraction may be empty + for non-standard formats; that is acceptable +- Fix candidates are suggestions, not guaranteed correct fixes +- Manual fallback always generated so the iteration loop is never stuck +- Max 600 lines per header diff --git a/sprint115_plan.md b/sprint115_plan.md new file mode 100644 index 0000000..9ea4628 --- /dev/null +++ b/sprint115_plan.md @@ -0,0 +1,196 @@ +# Sprint 115 Plan: Requirements Derivation from AST + +## Context + +Environment setup is currently manual and implicit. A developer knows their +project needs Python 3.11, numpy, and a running PostgreSQL instance because +they remember building it — not because the system can read that from the code. + +Sprint 115 makes requirements derivation deterministic: given an AST, walk all +import/include/dependency nodes and produce a structured `RequirementsManifest` +— a language-agnostic list of what the project needs to run. From that manifest, +generate an idempotent setup script in the appropriate package manager format +(apt, pip, npm, cargo, cmake/vcpkg). Cross-reference the manifest against an +`EnvironmentSnapshot` (Sprint 112) to produce a gap report: what's missing, +what's the wrong version, what's ready. + +This is the "environment as code" step: the project's needs become a +machine-readable artifact that any agent can check, compare, and act on. + +--- + +## Goals + +1. Define `RequirementsManifest` schema derived from the AST +2. Walk all dependency nodes across supported languages +3. Emit setup scripts per package manager (apt, pip, npm, cargo, vcpkg) +4. Cross-reference manifest against an EnvironmentSnapshot to get a gap report +5. Expose derivation and gap report as MCP tools + +--- + +## Steps + +### Step 1379: `RequirementsManifest` schema (12 tests) + +Create `editor/src/RequirementsManifest.h`. + +Fields: `manifest_id`, `source_file` (optional — if derived from single file), +`workspace` (optional — if derived from full project), `language_runtimes[]` +(name, min_version, source), `packages[]` (name, version_constraint, +package_manager: apt/pip/npm/cargo/vcpkg/brew/unknown), +`system_services[]` (name, port — e.g. postgres:5432, redis:6379), +`env_vars_required[]` (key, description, has_default: bool), +`derived_at`, `schema_version`. + +Tests (12): constructable with defaults, manifest_id non-empty, packages +list starts empty, runtimes list starts empty, services list starts empty, +env_vars list starts empty, JSON serialization, JSON deserialization, +package_manager enum round-trips, unknown manager passthrough, schema_version +present, deterministic output. + +### Step 1380: Import/include dependency walker (10 tests) + +Create `editor/src/DependencyWalker.h`. + +Walks an open AST, finds all import/include/require nodes, and extracts +package names. Language-specific strategies: +- Python: `import X`, `from X import Y` → package name is top-level module +- C/C++: `#include ` → system header; `#include "X"` → local (skip) +- JavaScript/TypeScript: `require('X')`, `import ... from 'X'` → npm package +- Rust: `extern crate X`, `use X::` → cargo crate +- Java: `import X.Y.Z` → Maven group + +Stdlib modules filtered via a known-stdlib list per language (extensible). + +Tests (10): Python import extracted, Python stdlib filtered, C++ system header +extracted, C++ local include skipped, JS require extracted, TS import extracted, +Rust extern crate extracted, Java import extracted, batch walk across multiple +files, deterministic output order. + +### Step 1381: Runtime version inferrer (10 tests) + +Create `editor/src/RuntimeVersionInferrer.h`. + +Infers language runtime requirements from project configuration files: +- Python: `pyproject.toml` `requires-python`, `.python-version`, `setup.py` +- Node.js: `package.json` `engines.node`, `.nvmrc` +- Rust: `rust-toolchain.toml` +- C++: CMake `CMAKE_CXX_STANDARD` +- Java: `pom.xml` `maven.compiler.source` + +Falls back to "any recent version" if no constraint found (min_version = ""). + +Tests (10): pyproject.toml version read, .python-version read, package.json +engines read, .nvmrc read, rust-toolchain.toml read, cmake standard read, +pom.xml source read, missing file returns no-constraint, multiple constraints +take strictest, deterministic output. + +### Step 1382: Service dependency detector (10 tests) + +Create `editor/src/ServiceDependencyDetector.h`. + +Detects service dependencies from config files and connection string patterns +in source. Scans `.env`, `config.yaml`, `appsettings.json` for patterns like +`POSTGRES_HOST`, `REDIS_URL`, `MONGO_URI`, `DATABASE_URL`. Maps to +`SystemServiceRecord` (name, port). + +Also detects from source: `psycopg2.connect(...)`, `mongoose.connect(...)`, +`redis.Redis(...)` call patterns in the AST. + +Tests (10): postgres from .env POSTGRES_HOST, redis from REDIS_URL, mongo +from MONGO_URI, mysql from DATABASE_URL pattern, psycopg2 call in AST, +mongoose call in AST, redis.Redis call in AST, no false positives on +unrelated config, deterministic output, unknown service captured as unknown. + +### Step 1383: Setup script generator (10 tests) + +Create `editor/src/SetupScriptGenerator.h`. + +Given a `RequirementsManifest`, generates an idempotent shell script per +requested package manager. Scripts use guard patterns (apt: check dpkg, +pip: check importlib, npm: check node_modules, cargo: check cargo metadata). + +Output: `SetupScript` with fields `package_manager`, `script_text`, +`estimated_packages` (count), `requires_sudo` (bool). + +One `whetstone_generate_setup_script` call may return multiple scripts +(one per relevant package manager detected from the manifest). + +Tests (10): apt script generated for apt packages, pip script generated, +npm script generated, cargo script generated, idempotent guard present in +all scripts, sudo flag set for apt, not set for pip/npm/cargo, empty package +list produces no-op script, deterministic output, script text is valid shell. + +### Step 1384: Requirements gap report (8 tests) + +Create `editor/src/RequirementsGapReport.h`. + +Cross-references `RequirementsManifest` against `EnvironmentSnapshot`. + +Fields: `ready` (bool), `satisfied[]` (package name), +`missing[]` (name + required_version + package_manager), +`version_mismatch[]` (name + required + installed), +`unverifiable[]` (packages where installed version can't be confirmed), +`service_gaps[]` (services in manifest not found in snapshot processes). + +Tests (8): ready true when all satisfied, missing package detected, version +mismatch detected, correct version passes, service gap detected, unverifiable +when probe can't confirm, JSON round-trip, deterministic output. + +### Step 1385: `whetstone_derive_requirements` MCP tool (8 tests) + +Input: `{ "workspace": string?, "files": string[]? }` +Output: `RequirementsManifest` JSON. + +Walks all open buffers (or specified files), runs DependencyWalker + +RuntimeVersionInferrer + ServiceDependencyDetector, merges results. + +Tests (8): tool registered, packages field present, runtimes field present, +services field present, empty workspace returns empty manifest, single file +mode works, manifest_id returned, deterministic output. + +### Step 1386: `whetstone_generate_setup_script` MCP tool (8 tests) + +Input: `{ "manifest_id": string?, "manifest": RequirementsManifest? }` +Output: `{ "scripts": SetupScript[] }` + +Accepts either a previously derived manifest by ID or an inline manifest. + +Tests (8): tool registered, scripts returned, apt script present for C++ +project, pip script present for Python project, idempotent guards in output, +empty manifest returns empty scripts, manifest_id lookup works, deterministic. + +### Step 1387: `whetstone_verify_requirements` MCP tool (8 tests) + +Input: `{ "manifest_id": string, "snapshot_id": string }` +Output: `RequirementsGapReport` JSON. + +Loads both from store, computes gap report. + +Tests (8): tool registered, ready true when all installed, missing reported, +mismatch reported, service gap reported, missing manifest_id error, missing +snapshot_id error, deterministic output. + +### Step 1388: Sprint 115 integration summary + regression (8 tests) + +Create `editor/src/Sprint115IntegrationSummary.h`. + +End-to-end: open Python project → derive_requirements → snapshot_environment → +verify_requirements → generate_setup_script → verify gap report matches missing +packages in snapshot. + +Tests (8): headers constructable, tools registered, end-to-end flow works, +gap report accurate, setup script produced, no header exceeds 600 lines, +deterministic snapshots, full prior regression passes. + +--- + +## Architecture Gate + +- Dependency walker uses AST node traversal only — no regex on raw source text +- Service detection from config files uses ConfigFileProbe (Sprint 112) — + no duplicated parsing logic +- Setup scripts are never executed by Whetstone — generation only, execution + is the caller's responsibility +- Max 600 lines per header diff --git a/sprint116_plan.md b/sprint116_plan.md new file mode 100644 index 0000000..a50caa9 --- /dev/null +++ b/sprint116_plan.md @@ -0,0 +1,205 @@ +# 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 + +1. Define the orchestrator's decision policy (what to do given current state) +2. Implement automatic candidate selection and application +3. Handle escalation with a structured "what was tried" summary +4. Expose the orchestrator as a single MCP entry point +5. 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 `FixCandidate`s 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_type` +- `attempts_made` (count) +- `final_gap` (GapModel) +- `attempts_summary[]`: each attempt's action, outcome, gap_before, gap_after +- `top_remaining_candidates[]`: the best manual candidates not yet tried +- `escalation_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: +1. Load session from SessionStore +2. Run OrchestratorPolicy to get next action decision +3. Execute the action (build iteration / test iteration / env probe) +4. Run CandidateAutoSelector on result +5. Apply selected mutation to AST (if non-manual) +6. Record attempt, update session, persist +7. 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/.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 diff --git a/sprint117_plan.md b/sprint117_plan.md new file mode 100644 index 0000000..1d0b385 --- /dev/null +++ b/sprint117_plan.md @@ -0,0 +1,182 @@ +# Sprint 117 Plan: HiveMind Integration — Feedback Loop as Distributed Jobs + +## Context + +Sprints 111-116 implement the feedback loop as a local, in-process operation: +the orchestrator runs build/test commands on the same machine as Whetstone. +That works for single-node development but misses HiveMind's core value: the +right work runs on the right hardware node. + +Sprint 117 integrates the feedback loop with HiveMind's job dispatch model. +Instead of running `cmake --build` locally, the orchestrator can emit a +`build_job` to HiveMind and wait for the result. Instead of running `pytest` +locally, it emits a `test_job`. The orchestrator becomes a HiveMind +client — it submits iteration steps as jobs, polls for completion, and feeds +the results back into the session as `ObservationRecord`s. + +This is the natural boundary between the two systems: Whetstone owns the +*reasoning* (what to do next, what does the result mean, what to try next); +HiveMind owns the *execution* (run this on the GPU node, run that on the +NAS, parallelize across the fleet). + +--- + +## Goals + +1. Define a HiveMind job adapter for build, test, and probe iteration steps +2. Submit iteration steps as HiveMind jobs via the nexus HTTP API +3. Poll for job completion and map the result to ObservationRecord +4. Integrate the distributed path with the FeedbackLoopOrchestrator policy +5. Allow the orchestrator to run locally or distributed (config per session) + +--- + +## Steps + +### Step 1399: `HiveMindJobAdapter` schema and config (12 tests) + +Create `editor/src/HiveMindJobAdapter.h`. + +Config fields: `nexus_url` (e.g. http://localhost:8765), `capabilities[]` +(required capabilities for the job — forwarded to HiveMind requirements), +`bounty` (low/normal/high_priority), `poll_interval_s` (default 3), +`timeout_s` (default 300). + +Maps orchestrator action types to HiveMind job types: +- `build` → `"build_job"` with payload `{ "command": string, "workspace": string }` +- `test` → `"test_job"` with payload `{ "command": string, "workspace": string }` +- `probe` → `"probe_job"` with payload `{ "workspace": string }` + +Tests (12): config constructable, nexus_url stored, capabilities stored, +bounty defaults to normal, poll_interval defaults to 3, timeout defaults +to 300, build action maps to build_job, test action maps to test_job, +probe action maps to probe_job, payload fields populated, JSON round-trip, +deterministic output. + +### Step 1400: Job submission via nexus HTTP API (10 tests) + +Create `editor/src/NexusJobSubmitter.h`. + +Submits a HiveMind job via `POST {nexus_url}/jobs`. Uses libcurl (available +from Sprint 113 build subprocess pattern, or via HTTP client in stdlibs). +Returns `job_id` string on success. Handles: 200 OK (parse id), 4xx (error), +timeout (fail fast), network error (fail fast). + +Tests (10): submit produces job_id, invalid nexus_url returns error, 4xx +response returns structured error, timeout returns error, payload correctly +serialized, capabilities forwarded, bounty forwarded, job_type forwarded, +non-blocking submit (returns immediately after HTTP response), +deterministic request format. + +### Step 1401: Job result poller (10 tests) + +Create `editor/src/NexusJobPoller.h`. + +Polls `GET {nexus_url}/jobs/{job_id}` until status reaches `done` or `failed`. +Returns `JobPollResult`: `job_id`, `status` (done/failed/pending/claimed), +`output` (parsed from result payload — `output_file` content or inline), +`exit_code`, `duration_ms`. + +Respects poll_interval_s and timeout_s from HiveMindJobAdapter config. + +Tests (10): poll returns done on success, poll returns failed on failure, +poll respects timeout, poll respects interval, output field populated, +exit_code populated, pending status keeps polling, claimed status keeps +polling, network error returns error, deterministic result shape. + +### Step 1402: Result-to-ObservationRecord mapper (10 tests) + +Create `editor/src/JobResultMapper.h`. + +Maps a `JobPollResult` to an `ObservationRecord`: +- `source` = build_output / test_output / env_probe (from action type) +- `raw_text` = output field (truncated at 16KB) +- `exit_code` = from JobPollResult +- `duration_ms` = from JobPollResult +- `structured_data` = parsed by BuildOutputParser or TestOutputParser + depending on action type + +Tests (10): build result maps to ObservationRecord with source=build_output, +test result maps with source=test_output, probe result maps with +source=env_probe, raw_text truncated at limit, exit_code mapped, duration_ms +mapped, structured_data populated by parser, failed job maps to exit_code!=0, +JSON round-trip of mapped record, deterministic output. + +### Step 1403: Distributed path in FeedbackLoopOrchestrator (10 tests) + +Extend `FeedbackLoopOrchestrator.h` to support a `distributed` mode +(set per session via config). + +When distributed=true: +- `step()` submits a HiveMind job instead of running subprocess locally +- Returns immediately with `session_status=active, action=submitted` + (the loop is async — caller must step again to poll) +- On next step: if a pending job exists for the session, poll it; + if done, map result and continue; if timeout, escalate + +When distributed=false (default): existing local subprocess behavior unchanged. + +Tests (10): distributed mode submits job not subprocess, immediate return +on submit, subsequent step polls existing job, done result processed correctly, +failed result processed, timeout escalates, local mode still works, config +persisted in session, mode switch between sessions works, deterministic policy. + +### Step 1404: `whetstone_configure_hivemind` MCP tool (8 tests) + +Input: `{ "nexus_url": string, "capabilities": string[]?, + "bounty": string?, "timeout_s": int? }` +Output: `{ "configured": true, "nexus_url": string }` + +Persists HiveMind config to `.whetstone/hivemind_config.json` in workspace. +All sessions created after this call use distributed mode by default. + +Tests (8): tool registered, config persisted, nexus_url stored, capabilities +stored, bounty defaulted to normal, timeout defaulted to 300, overwrite works, +deterministic output. + +### Step 1405: `whetstone_submit_iteration_job` MCP tool (8 tests) + +Input: `{ "session_id": string, "action_type": string, + "command": string, "workspace": string }` +Output: `{ "job_id": string, "attempt_id": string, "status": "submitted" }` + +Submits one iteration step as a HiveMind job and records the pending attempt. + +Tests (8): tool registered, job_id returned, attempt_id returned, status +is submitted, attempt recorded in session, nexus_url from config used, +missing config falls back to local, deterministic output. + +### Step 1406: `whetstone_poll_iteration_job` MCP tool (8 tests) + +Input: `{ "session_id": string, "attempt_id": string }` +Output: `{ "job_status": string, "session_status": string, + "gap": GapModel?, "candidates": FixCandidate[]? }` + +Polls the HiveMind job associated with the attempt, processes result if done. + +Tests (8): tool registered, pending returns job_status=pending, done processes +result, failed processes failure, session_status updated on done, gap present +after processing, candidates present on failure, deterministic output. + +### Step 1407: Sprint 117 integration summary + regression (8 tests) + +Create `editor/src/Sprint117IntegrationSummary.h`. + +End-to-end distributed test (against mock nexus): start session with +distributed=true → submit_iteration_job → poll_iteration_job (mock returns +done) → verify attempt recorded + candidates returned. + +Tests (8): headers constructable, tools registered, distributed end-to-end +mock test passes, local mode still works, config round-trip, no header exceeds +600 lines, deterministic snapshots, full prior regression passes. + +--- + +## Architecture Gate + +- HiveMind nexus URL is never hardcoded — always from config or MCP input +- Distributed mode does not block the MCP server — jobs are async by design +- Local mode (distributed=false) remains the default until explicitly configured +- No HiveMind-specific types leak into the core orchestrator headers — + the adapter pattern keeps the boundary clean +- Max 600 lines per header