# QA Plan: session-robustness-and-dox — master..feat/session-robustness-and-dox **Status:** DRAFT **Run date / operator:** **BACKLOG item:** feat/session-robustness-and-dox full-branch sweep Commits under test: ``` c8c2521 fix(kernel,inference): reliable artifact emission for local models af9da3c fix(inference): raise llama HTTP request timeout 600s -> 1800s 5e0c7df feat(server): REST stage-approval route POST /sessions/{id}/approve e72051a feat(kernel): emit_artifact tool + actionable validation feedback 0652d87 fix(inference): salvage truncated/malformed tool-call JSON from content 4737300 fix(server): anchor static-registry file_read to the workspace 82674e8 feat(inference): capability-aware routing for tool-heavy stages 238d353 feat(qa): remote NIM provider + headless-QA robustness e0f623a feat(workflow): tasked execution loop replaces role_pipeline a00bd4a feat(tools): propose_scope recalibration valve for the execution loop 3e44c6d feat(kernel): deterministic per-task claim stage + per-task write scope c1e4c7b feat(transitions): tasks_ready predicate for execution loop d26f20c docs(dox): build out the DOX AGENTS.md hierarchy 1cb7fec feat(kernel): read-only mode after read-before-write + mandatory task 201b599 fix(inference): recover tool calls emitted as JSON in message content ccfa4b4 feat(tui): composer soft-wrap, alt+backspace, bracketed paste, borders 8abe7d9 fix(tui): collapse multiline tool summaries so action rows don't stripe ``` --- ## Preconditions - [x] **server build/branch:** `feat/session-robustness-and-dox`, `:apps:server:run` on :8080 — _rebuild only with the server stopped_ - [x] **llama-server + model:** router = gemma-4-12B-it-qat-UD-Q4_K_XL :10000 (tool-capable, ~48 tok/s), narrator = LFM2.5-1.2B :10001 - [ ] **external deps:** none required for the core loop; capability-aware routing (§C8) is only meaningfully exercised if **two** tool-capable providers are configured — otherwise it's a single-candidate no-op. OpenAI-compat/NIM path (238d353) needs a remote endpoint; **out of scope** unless one is wired. - [ ] **config synced:** `~/.config/correx/workflows/` must contain the NEW `task_planning.toml` + `prompts/task_planner.md` and the REWRITTEN `role_pipeline.toml` (execution loop). Copy from `examples/workflows/` before running — stale role_pipeline = wrong graph. - [ ] **fixtures/seed:** a real workspace with a `.correx/project.toml` and a small, genuinely-actionable request (so `task_decompose` produces >1 task and the loop iterates). ## Acceptance gate (one sentence) > The branch is correct **iff** a real request drives the execution loop end-to-end — > planner emits a task graph, each task is deterministically claimed, writes are scoped to > the claimed task's `affected_paths` (and blocked outside it / in read-only mode), artifacts > emit reliably on the local model, and a headless `POST /approve` clears an approval gate — > every step evidenced by a recorded event. ## Checks | # | Action | Expected observable evidence | Result | |---|--------|------------------------------|--------| | 1 | Start a `task_planning` session on a real request | `correx events ` shows the `planner` stage; a `task_decompose` tool invocation succeeds and task-created events appear; stage cannot complete without it (`require_task_decompose` gate) — try a run where the model skips decompose and confirm the stage is **blocked**, not completed | | | 2 | Start a `role_pipeline` (execution loop) session | Stage chain analyst→architect→decomposer→implementer→reviewer in the log; `decomposer-to-implementer` fires on `tasks_ready` (not always_true); loop re-enters implementer via `review-2-more` while tasks remain, exits via `review-3-done` when drained | | | 3 | Watch the per-task claim stage (`claimTask=true`) | On each implementer entry, a claim record for the next ready task + its context bundle injected as **L0** (`sourceType=claimedTask`); the loop advances by claim events, not model memory — verify the claimed task id changes across iterations | | | 4 | Let the implementer write a file inside the claimed task's `affected_paths` | `FileWriteTool` invocation ASSESSED and allowed; file appears on disk under the workspace | | | 5 | Force a write **outside** the claimed scope | `ToolCallAssessedEvent` blocks it (scope violation); the write does not land on disk; feedback surfaced to the stage | | | 6 | Trigger read-only mode (a stage that reads before any task is claimed / after read-before-write) | `isReadOnlyMode` denies a subsequent write: `ToolCallAssessedEvent` with the read-only denial; no file mutation event | | | 7 | Have the implementer call `propose_scope` with a `paths`/`reason` | Tool records a scope-widen proposal on the task; **operator approval required** (approval gate fires); on approve, paths added to `affected_paths` and the previously-blocked write now lands | | | 8 | Confirm artifact emission on the local model (analysis/design/patch) | Each `*_validated` transition fires because the artifact actually emitted — `artifact_validated` events present; no stage stuck retrying because the artifact never materialized (the c8c2521 fix) | | | 9 | Feed a truncated/malformed tool-call (or observe a real one from gemma) | Salvage path recovers it: the tool call executes instead of the turn failing; log shows the salvage of JSON-in-content (0652d87 / 201b599) | | | 10 | With a session suspended on an approval, `POST /sessions/{id}/approve` `{"decision":"APPROVE"}` (no requestId) | HTTP 200; server resolves the pending requestId itself; the gate clears and the session advances — no WebSocket needed. Also test `REJECT` (session takes reject path) and a bad `decision` → 400, and no-pending → 404 | | | 11 | `POST /approve` with `{"decision":"STEER","steeringNote":"..."}` | Treated as APPROVE with the note attached; note recorded on the approval response | | | 12 | (If 2 tool-capable providers configured) run a tool-heavy stage | Routing selects the higher tool-capability provider, not just any healthy one; check `/metrics/tool-reliability` reflects observed per-model reliability | | | 13 | TUI composer smoke: soft-wrap a long line, alt+backspace, bracketed paste | Composer wraps within borders; alt+backspace deletes a word; pasted multiline text lands as one block; multiline tool summaries render collapsed (no striped action rows) | | | 14 | `correx replay ` on a completed loop session | Double-read digest matches — the claim/scope/read-only logic is derived from recorded events, replay-stable | | ## Out of scope (explicitly NOT covered this pass) - OpenAI-compat / remote NIM provider (238d353) — no remote endpoint wired; unit-tested only. - Capability-aware routing real differentiation (§C12) unless a second tool-capable provider is configured; single-provider makes it a no-op. - The DOX AGENTS.md hierarchy (d26f20c) — docs only, nothing to exercise at runtime. - Layer-2 LLM annotation of tool-intent; the AMD/ROCm perf envelope. ## Disposition - **PASS** → move the BACKLOG entry into `RETRO.md` with run date + cited evidence. Set Status: PASSED. - **FAIL** → file each miss as a numbered `BACKLOG.md` finding (action + repro + wrong/missing signal), fix, re-run only failed checks. Status: FAILED until green.