# CORREX — RETRO (archive of resolved BACKLOG items)

Counterpart to `BACKLOG.md` (the live outstanding list). Items here are **built and
unit-verified** (Gradle green). Where a live-QA gate applies, that's called out as a
follow-up — this sandbox has no local model / SearXNG / GPU / network, so live-QA
could not be run here.

---

## 2026-06-22 — BACKLOG hygiene reconcile + operator-requested TUI wave (branch `feat/backlog-burndown`)

**Hygiene reconcile.** Per `BACKLOG.md`'s own maintenance rule, cut the resolved body bullets
that were left ticked-but-in-place after the 2026-06-20 burndown and 2026-06-21 grants work.
Sections affected: §A (health probes), §B (static-first infra, `CritiqueFinding`, architect
contradiction-check), §C (A1/A2 gate code), §D (batch fetch-approval, source events, dynamic
egress), §E (render matrix, markdown, session list, idea promotion), §G (turnId/probe/narration),
§I (`.cs` symbols, scene validator). Partially-done items were **narrowed to their live
follow-up** rather than deleted: §B5 → "static_check stage emitter only", §B6/§C-A3 → "the
`CritiqueOutcomeCorrelatedEvent` producer only", §C-A1 → "planner-prompt activation only". The
full commit map stays in the BACKLOG "✅ RESOLVED" index; detail is in the tables below.

**Operator-requested TUI wave (archived here — never BACKLOG items, like the grants work).**
A run of TUI/UX features added on the branch after the burndown, each compiled + `go vet`/`go
test` green and rendered via `cmd/preview` before commit:

| Commit | What |
|--------|------|
| `9143d55` | Global input history + stop idle redraws (copy-safe — no redraw churn while selecting text). |
| `85bfddf` | Slash-command menu in the chat input. |
| `2662156` | Transparent modal backdrops (overlays no longer paint an opaque box over the transcript). |
| `ba7ac06` | Transcript message-jump (`^↑`/`^↓` to your prev/next message) + OSC52 clipboard copy. |
| `65e5d88` | **Server:** `ListFiles`/`FileList` protocol backing the TUI `@` picker. |
| `04d7e26` | `@` file-reference picker in the input. |
| `c616982` | **Context:** inject `CLAUDE.md` / `AGENTS.md` as L0 standing context. |
| `54f94a5` | Command-palette keybinds + configurable (hideable) status bar. |
| `6a06f2e` | Inline action rows in the OUTPUT transcript (tool results / approvals / writes / grants as `RouterEntry` role `action`, interleaved by arrival). |
| `f14a83c` | Drop the inline tool-*start* rows (they doubled up with the result rows). |
| `531910d` | Transcript free-scroll (`PgUp/PgDn`, `^u/^d`, `esc` snaps to tail-follow; clamped via `outputViewport()`/`buildTranscriptRows`, 2 tests) + help overlay (`?`) + event-inspector `/` filter. |
| `db27b34` | Page-scroll the `^x` diff/preview/command fullscreen viewer (`↑↓` line, `PgUp/PgDn` page, `^u/^d` half, `g/G` ends; `scrollDiff()` clamp, 2 tests) + advertise it in the modal hint and the `?` help overlay. Closes the §E approval-ergonomics re-audit. |
| `d247b19` | **Bubble Tea v1 → v2** (`charm.land/{bubbletea,lipgloss}/v2`, Go 1.25). A key-handling shim (`key.go`: v2 `KeyPressMsg` → v1-shaped `keyMsg` at the single `Update` boundary) keeps ~190 match sites untouched. Other v2 breaks handled: `lipgloss.Color` is now a func returning `color.Color` (retyped fields/params); `View()` returns `tea.View` (`render()` builds the string, `View()` carries `AltScreen` — alt-screen is a per-frame field now, not a program option); `WithWhitespaceStyle` replaced `WithWhitespace{Background,Foreground}`; preview dropped `SetColorProfile` (v2 = truecolor by default). Enables literal **Shift+Enter** newline via v2 kitty disambiguation (`Ctrl+J`/`Alt+Enter` kept as non-kitty fallback). Live-QA: `docs/qa/QA-tui-v2.md`. |
| `85af2c6` | **Real terminal cursor in composer + window title.** `View.Cursor` replaces the frame-animated `▏`: a width-1 marker rune (U+E123) is located in the fully-composited frame → blinking `CursorBar` at `(X,Y)`; static `render()` resolves it back to `▏` so screenshots/goldens are unchanged. Nil in normal mode (hidden, vim-correct), suppressed when a modal is open (which keeps `editMode==Insert`). `animating()` no longer ticks in insert mode — the terminal blinks the cursor itself, so an idle insert screen holds still (native text selection survives). `View.WindowTitle` = active session/model. New `cursor_test.go` (3) + updated animating-gate test. |

These are mostly deterministic render/UX changes (verifiable by `go test` + `cmd/preview`).
The v2 migration + native cursor are unit-verified the same way, but their *behavioral* wins
(Shift+Enter, the real blinking cursor, no regressions across the new render layer) need a real
kitty terminal — that's the live-QA gate in `docs/qa/QA-tui-v2.md`.

**§E approval-ergonomics re-audit — DONE (`db27b34`).** Re-checked the band after it gained the
grant scope-picker (`A`) and inline action rows: the spec still holds — single-key `y/n/e` for
T0–T2, mandatory second-key confirm for T3+ (`HighTier`), navigable queue with an `i/n` indicator,
docked band never modal-stacks. All covered green by `approval_test.go`. The one gap was the diff
viewer scrolling one line at a time; `db27b34` adds paging. Item moved out of BACKLOG.

---

## 2026-06-22 — reviewer reliability: static_check stage + critique calibration producer (branch `feat/backlog-burndown`)

Two deterministic role-reliability tracks whose consumer halves already existed but had nothing
feeding them. Both built + unit-verified (Gradle green, detekt clean); their *activations* are
model-/sandbox-gated and stay narrowed in BACKLOG.

| Commit | What | Item |
|--------|------|------|
| `e46777e` | **§B-§5 deterministic `static_check` stage seam + emitter.** `StaticCheckStageExecutor` runs a configured tool (`static_tool`/`static_argv` via stage metadata) through the existing `StaticAnalysisRunner` and emits `StaticFindingsRecordedEvent`; `DefaultSessionOrchestrator.enterStage` dispatches any `metadata["stage_type"]=="static_check"` stage to it instead of the LLM subagent and advances on the unconditional exit edge; `TomlWorkflowLoader` gained `stage_type`/`static_tool`/`static_argv` fields; `role_pipeline.toml` carries a `static_check` stage between implementer and reviewer. **No-op until** a sandboxed, workspace-scoped `CommandRunner` is wired + `static_argv` set (live-QA-gated). Tests: `StaticCheckStageExecutorTest` (4) + `StaticCheckStageTest` integration (1). | B §5 |
| `e46777e` | **§B-§6 / A3 critique-outcome producer.** New `CritiqueFindingsRecordedEvent` (+ `CritiqueVerdict`) is the producing side — a critic's findings + verdict per iteration, with `modelHash`. `CritiqueOutcomeCorrelator` (pure, in the reviewer-loop runtime) decides UPHELD / DISMISSED / INCONCLUSIVE per critic and finding id; a hook in `completeWorkflow`/`failWorkflow` correlates recorded findings into `CritiqueOutcomeCorrelatedEvent`s at loop resolution (no-op + idempotent), feeding the existing `:core:critique` calibration projection. **Dormant until** the reviewer/plan-critic emit `CritiqueFindingsRecordedEvent`s (model-gated). Tests: `CritiqueOutcomeCorrelatorTest` (8) + `CritiqueCalibrationWiringTest` integration (1). | B §6 / C-A3 |

Both follow the same shape as the earlier dormant-activation work: the deterministic seam + the
event wiring land and are unit-proven now; the LLM-/sandbox-side activation is the live-QA-gated
follow-up. `docs/qa/QA-reviewer-static-first.md` updated (no longer "producing stage not built").

---

## 2026-06-21 — wider grants + revoke (branch `feat/backlog-burndown`)

Operator-requested (not a prior BACKLOG item): grants that outlive a single session, plus a
revoke path. Two commits, each compiled + unit-tested green.

| Commit | What |
|--------|------|
| `c36d41b` | **Cross-session grant scopes (PROJECT/GLOBAL) + revoke.** `GrantScope.GLOBAL(toolName)` added; `PROJECT` made tool-bound. Engine `scopeMatches`: GLOBAL matches the bound tool anywhere, PROJECT when the session's `projectId` (derived from the bound workspace root via `ProjectIdentity.of`) AND tool match. New reserved `GRANT_LEDGER_SESSION_ID` stream holds PROJECT/GLOBAL grants; the approval gate unions the ledger's grants with the session's. `RevokeGrant` → `ApprovalGrantExpiredEvent` on the ledger (reducer already drops it); `ListGrants`/`GrantList` expose the active standing grants. **Server-side T2 grant ceiling removed** per operator request — a grant may now authorize any tier (incl. T3/T4); tool-binding is the remaining guard. New `GrantScopeMatchingTest` (5). |
| `8df0ec7` | **TUI: scope picker (A) + standing-grants viewer.** `A` (approve-always) opens a session/project/global picker (SESSION default = old behaviour); `p`/`g` create wider grants. `grants` palette command + `G` shortcut open `OverlayGrants` listing scope/tool/tiers/path with `x`/enter to revoke. `protocol.go`: `TypeGrantList`+`GrantDto`, `RevokeGrant`/`ListGrants` encoders. Both overlays rendered via `cmd/preview` (kinds `grants`, `grant-scope`). |

**Why the design:** session grants are event-sourced per-session and die with the session;
project/global grants must outlive any one session, so they live in a dedicated ledger stream
the gate folds in — keeps grants in the audit log (invariant #9) rather than config.

**⚠️ Live-QA gate (move out of RETRO once passed):** unit tests cover the engine matching,
reducer, and ledger fold, but the *cross-session* behaviour — create a GLOBAL/PROJECT grant in
session A, confirm a **different** session B auto-clears that tool, then revoke and confirm it
prompts again — needs a running server + model. Not runnable in this sandbox.

---

## 2026-06-20 — backlog burndown (branch `feat/backlog-burndown`)

Nine tracks, each compiled + unit-tested green before commit.

| Commit | Track | BACKLOG ref |
|--------|-------|-------------|
| `f783eed` | `CritiqueFinding`/`CritiqueSeverity`/`CritiqueRole` type + `CritiqueOutcomeCorrelatedEvent` + `core/critique` calibration projection (state/reducer/projector) | B §6 + C-A3 |
| `784accb` | `repomap:` turnId namespace requires trailing `:` (no `/repo`↔`/repo2` collision; L3RepoKnowledgeRetriever filter + ProjectMemoryService write) + WorkspaceStateProbe newline fingerprint | G |
| `266bbf0` | `EventStoreLatencyProbe` (wired) + `LlamaServerHealthProbe` (+ injectable `LlamaLivenessClient`) + `HealthConfig.eventStoreWarnLatencyMs` | A §4 |
| `f715ffa` | `.cs` / Godot-Mono type symbols in `RepoMapIndexer.SYMBOL_PATTERNS` | I |
| `0e251d6` | `SceneValidator` — structural `.tscn`/`.tres` (header well-formedness, ExtResource/SubResource id resolution, node parent integrity) | I |
| `1df7af5` | Brief echo-back gate: `BriefEchoMismatchEvent` + `BriefEchoComparator`/`BriefEchoExtractor` + `checkBriefEcho` orchestrator gate | C-A1 |
| `1a1b5cc` | Stage-level plan checkpointing: `StageCheckpointPassed/FailedEvent` + `StageCheckpointReconciler` + `emitStageCheckpoint` (gated on a locked plan) | C-A2 |
| `eae0a0c` | Architect contradiction-check (display-only): `PossibleContradictionFlaggedEvent` + L3-backed `ArchitectContradictionChecker` | B §4 |
| `b098d87` | Dedicated `SourceFetchedEvent` / `LowQualityExtractionEvent` (promoted from `ToolExecutionCompletedEvent` metadata; additive), emitted from `SandboxedToolExecutor` | D |
| `027ff1f` | Dynamic per-session egress allowlist: `EgressHostsGrantedEvent` + `EgressAllowlist` union helper + `EgressAllowlistProjection`; `NetworkHostRule` delegates to the union | D |
| `447fc7a` | Reviewer static-first: `StaticFindingsRecordedEvent` + `StaticAnalysisRunner` (injectable `CommandRunner`; compiler/ktlint/detekt parsers) + `excludeStaticFindingsFromReview` filter, live-wired into reviewer context assembly | B §5 |
| `eff8f8d` | Narration-lane lag: stale pause narrations dropped per-approval-request on resolve / per-session on resume (replay-safe) | G |
| `1e6699a` | tui-go `go.mod` `go 1.26` → `1.24.2` (1.26 toolchain unreleased/unfetchable) — unblocks Go build/test on released toolchains | E (enabler) |
| `da72ee5` | tui-go: markdown rendering for chat turns via `glamour` (dark, per-width memoized), plain-text fallback; other roles/UI untouched | E |
| `1f7a5d9` | tui-go session list / resume view: `R` overlay over `GET /sessions`, resume via `POST /sessions/{id}/resume` onto the existing global stream | E |
| `2a99e15` | tui-go per-event render-matrix golden tests (37 cases) + a coverage guard that parses `protocol.go` so any new event type without a golden fails | E §2 |
| `d5afcd3` | tui-go approval ergonomics: navigable pending-approval queue (no modal stacking) + `y/n/e` keys + tier-gated T3+ arm/confirm | E §3 |
| `641051b` | Plan-derived diff manifest: `PlanStage.writes` + `PlanDerivedManifest.deriveAllowedWrites`/`combine`, unioned into `StageConfig.writeManifest` at compile, enforced by the existing containment matcher | B §2 |
| `ab7e4be` | Egress allowlist **activation**: `resolveSessionEgressHosts` threads the session's `EgressAllowlistProjection` state into `ToolCallAssessmentInput` at `runPlane2Assessment`, so `NetworkHostRule` now consults the live per-session union (was fed `emptySet()`) | D (activation) |
| `4b0024f` | Batch source-list fetch-approval: `POST /sessions/{id}/approve-sources` → `SourceListHosts.extract` (java.net.URI, lower-cased, ports stripped, dossier-line tolerant) → one `EgressHostsGrantedEvent`; live allowlist then auto-clears those web_fetches (no per-URL T2) | D |
| `f107ff5` | Idea-board → project-profile promotion: `IdeaPromotedEvent` (board tombstone, like discard) + `ProjectProfileWriter` (escape-aware TOML serialize/write) + `ProjectProfile.withConvention` (deduped) + server `PromoteIdea` handler (loads profile, appends idea text, writes `.correx/project.toml`); also made `SimpleToml`'s reader unescape `\\`/`\"` symmetrically with the writers (fixes pre-existing read/write asymmetry) | E |
| `4e08510` | Architect contradiction-check **activation**: `ServerModule.start()` subscribes to the architect stage's `design` `ArtifactCreatedEvent`, resolves decision text (design-schema `approach` field, capped fallback), runs `ArchitectContradictionChecker.check`, appends the non-null `PossibleContradictionFlaggedEvent`. Live-only, never halts. Checker built in `Main.kt` (gated on `project.enabled`) and threaded into `ServerModule`. Replaces the `Main.kt` `TODO(wiring)` | B §4 (activation) |
| `64a90e7` | Llama-probe **registration**: `LlamaServerHealthProbe` (266bbf0) wired into the health monitor when a `llamacpp` provider URL is configured — dedicated CIO client (3s timeout, shutdown-hook close) + `HttpLlamaLivenessClient`. Gated so a model-less box reports no spurious `LLAMA_SERVER` DEGRADED. Replaces the `Main.kt` `TODO(wiring)` | A §4 (activation) |

### Follow-ups created this pass (refiled into BACKLOG)
- **C-A1 activation** — `planner.md` must emit a `brief_echo` block and `role_pipeline.toml` must set `briefEcho="true"` / `briefEchoSource` for the gate to fire live (model-dependent → live-QA).
- **C-A3 producer** — nothing emits `CritiqueOutcomeCorrelatedEvent` yet; the calibration projection has no live feed until a reviewer-loop producer records finding outcomes.
- **B §4 wiring** — ✅ DONE (`4e08510`): `ServerModule.start()` now subscribes to the architect `design` artifact and emits the flag. (In-session L3 embedding is NOT needed — the checker filters to PRIOR sessions by design, so it works against prior runs' distilled decisions.)
- **A §4 llama wiring** — ✅ DONE (`64a90e7`): the probe is registered in the health monitor (gated on a configured `llamacpp` provider URL) via `HttpLlamaLivenessClient`. (tokens/sec telemetry still rides liveness-only — the `/health` endpoint reports reachability; a throughput feed remains future work but the spec's liveness probe is live.)
- **D egress wiring** — ✅ DONE (`ab7e4be`): session egress state is now threaded into `ToolCallAssessmentInput` at `runPlane2Assessment`, so `NetworkHostRule` consults the live union. Batch fetch-approval ✅ DONE (`4b0024f`): `POST /sessions/{id}/approve-sources` emits one `EgressHostsGrantedEvent` for the whole source list.
- **B §5 static_check stage** — runner/event/filter complete and the filter is live, but no workflow stage emits `StaticFindingsRecordedEvent` yet (`// TODO(wiring)` in `StaticAnalysisRunner.kt`); needs an event-emitting deterministic stage-type seam.
- **E toolchain** — tui-go now builds via auto-downloaded go1.24.2 (`GOTOOLCHAIN=auto`). The committed directive fix assumes 1.26 was unintended; bump if a specific newer release is actually wanted.

### Environment note
- A `~/.gradle/gradle.properties` machine-local override was added on this box (`-Xmx768m`, `parallel=false`, kotlin daemon `-Xmx640m`) because the repo's `-Xmx4G` OOM-killed the foreground process on a 1.9 GB machine. **Not committed** — repo defaults intact for higher-RAM machines.
