This catalog locks in verified debt findings. Add entries only after collecting concrete evidence.

Each entry is checklist-shaped so remediation can be tracked. These are implementation tasks, not architectural essays: keep findings concise, concrete, checkable, and tied to regression prevention.

Entry shape: checklist title line, summary evidence paragraph, impact paragraph, remediation task paragraph, regression-prevention paragraph.

## P1 Findings

- [x] `TD-001` [P1] RPC endpoint contract is hand-mirrored across protocol, DTOs, client, gateway, and tests.

- [x] `TD-002` [P1] `server/transport/gateway.go` is a policy-heavy dispatch god file.

- [x] `TD-003` [P1] `cli/app` is a god package owning too much frontend and server attachment behavior.

- [x] `TD-004` [P1] `server/core/core.go` exposes `Core` as a service locator and composition god object.

- [x] `TD-005` [P1] `cli/tui/model.go` stores transcript, render caches, selection, scroll, and dirty state in one mutable graph.

- [x] `TD-006` [P1] Runtime state ownership remains over-centralized across the engine and registry.

- [x] `TD-009` [P1] CLI packages import server-owned packages beyond the intended client boundary.

- [x] `TD-010` [P1] `shared/serverapi` mixes wire DTOs with server-owned service interfaces and headless runtime execution.

- [x] `TD-011` [P1] `shared/clientui` contained frontend reducer behavior instead of only client-facing DTOs.

- [x] `TD-012` [P1] Session read models branch between live runtime and dormant persistence in each query.

- [ ] `TD-013` [P1] Interactive, headless, remote, daemon, and embedded startup policies are duplicated in `cli/app`.

  `cli/app/session_server_target.go`, `cli/app/run_prompt_target.go`, `cli/app/headless_prompt_server.go`, and `cli/app/embedded_server.go` contain 1104 LoC of startup/attachment code. `startSessionServer` handles interactive remote discovery, reauth, daemon launch, and embedded fallback. `startRunPromptClient` handles headless workspace config, configured remote attach, daemon launch, auth readiness, headless workspace registration errors, and embedded fallback. `tryDialMatchingConfiguredRemoteWithRequirement` and `tryDialMatchingConfiguredRunPromptRemoteWithConfig` implement different attach and project-binding flows, with capability checks split between `configuredRemoteSupportsInteractiveSession` and `configuredRemoteSupportsRunPrompt`. `embeddedServer` exposes 27 methods, including auth manager access, every service client, project binding, runtime preparation, ownership, config, and reauthentication.

  Startup behavior can diverge by entrypoint. Interactive and headless paths use different registration requirements, capability gates, fallback decisions, timeouts, auth handling, and embedded binding behavior. The broad `embeddedServer` interface turns the CLI app layer into a service locator and makes it difficult to prove remote and embedded modes have identical semantics.

  Remediation task: move startup target resolution and runtime attachment policy into one server-attachment composition service shared by interactive and headless entrypoints. Define typed results for configured remote, launched daemon, embedded fallback, project/workspace binding state, auth readiness, ownership cleanup, and capability compatibility. Replace the broad `embeddedServer` interface with narrow attachments such as project binding, session launch, runtime control, run prompt, process streams, and auth bootstrap clients. Keep entrypoint-specific UX decisions at the edge, but make remote/daemon/embedded selection, workspace registration policy, fallback policy, and cleanup ownership use the same code path.

  Regression prevention must include table tests for interactive and headless startup across configured remote available/unavailable, incompatible capabilities, unregistered workspace, remote workspace selection, daemon launch success/failure, embedded fallback, auth bootstrap required, and cleanup on partial failures. Tests must assert the same target-resolution policy is used by both entrypoints where product decisions require parity, and import-boundary checks must prevent UI files from reaching directly into server composition to bypass the attachment service.

- [x] `TD-014` [P1] Legacy compatibility branches are permanent runtime behavior without schema-version ownership or sunset criteria.

- [x] `TD-015` [P0] Runtime and UI lifecycle states are encoded as parallel booleans that permit invalid combinations.

- [x] `TD-016` REMOVED

- [x] `TD-017` [P1] Session event-log utilities still materialize unbounded histories.

- [ ] `TD-018` [P0] Boundary validation and error taxonomy remain optional conventions instead of enforced contracts.

  Validation and typed error handling vary by boundary. `shared/apicontract/rpc_routes.go` derives `Route.ValidatesRequest` by reflecting whether the request type implements `Validate() error`, and `shared/apicontract/rpc_routes_test.go` only checks that the metadata matches reflection. `server/transport/gateway.go` uses `decodeAndHandle` to call `Validate` only when the decoded request happens to implement that interface, while `(*Gateway).serveRunPrompt` separately decodes `serverapi.RunPromptRequest` and forwards it without transport-level validation. Request examples without route-enforced validation include `serverapi.ProcessListRequest` and `serverapi.RunPromptRequest`; `serverapi.ProcessInlineOutputRequest.Validate` requires `process_id` but does not bound `MaxChars`. Tool and model boundaries also normalize malformed inputs by convention: `server/runtime/tool_executor.go` synthesizes missing tool-call IDs, continues unknown tools as per-call error results, special-cases web-search validation, and falls back through JSON adaptation for custom tool input; `server/llm/openai_http_output_parser.go` downgrades unknown output item types to `ResponseItemTypeOther` and returns empty parsed items for function/custom tool outputs missing required IDs or names. Extensibility registration validation sometimes fails through package-init or constructor panics instead of startup diagnostics: `cli/app/commands/commands.go` panics for command names containing whitespace, `server/tools/types.go` panics for nil registries, missing definitions, and duplicate handlers, `server/tools/definitions.go` panics during `init` for malformed tool contracts, and `server/llm/provider_factory.go` panics when provider/model contracts are malformed or duplicated. Config validation is similarly source-sensitive: `shared/config/config_load.go` applies file, env, then CLI state before validation, while `shared/config/config_validation.go` gates several checks through `hasExplicitSource`, which only treats `"file"` as explicit. Error mapping follows the same convention path: `shared/protocol/jsonrpc.go` exposes only `ResponseError{Code, Message}`, `server/transport/gateway.go` maps a fixed set of sentinel errors in `protocolError`, `shared/client/remote_rpc.go` reconstructs typed client errors only for known protocol codes, and `cli/kent/main.go` emits headless run JSON errors with only coarse code and message.

  Boundary correctness depends on every route, tool, model adapter, config source, and CLI caller remembering where validation and typed errors live. Invalid zero/default values can reach service code before validation, validation can happen after dependency resolution or other side effects in route-specific handlers, model-output corruption can be flattened into opaque entries, and clients cannot reliably distinguish retryable, auth, scope, validation, not-found, unavailable, unsupported, or operator-action-required failures when a service returns an unmapped error. This increases remote/embedded divergence risk and makes boundary behavior hard to audit from one source of truth.

  Remediation task: make validation and error semantics explicit at every public boundary. The authoritative route contract must declare validation policy, accepted request invariants, and typed error set before a route can be registered; gateway unary, progress, subscription, loopback, and remote clients must use the same generated or table-driven adapter. Tool invocation and model-response adapters must reject or surface malformed IDs, unknown item types, invalid schemas, and unsupported fields through typed diagnostics instead of silent normalization. Command, tool, and provider registries should keep fail-fast contract checks, but run them through explicit startup/composition validation that returns diagnosable typed errors instead of implicit package-init panics. Config/env/CLI validation must encode source-aware invariants explicitly for file, env, CLI, and generated defaults. Replace ad hoc sentinel checks with an error taxonomy that carries typed code, public message, retry/action policy, and optional structured details without leaking internal error strings.

  Regression prevention must include route-contract tests that fail when a request type with fields has no validation policy, when progress/subscription routes bypass validation, when request validation happens only in service code, or when a service can return a public error without a route-declared protocol mapping. Add tool/model adapter tests for missing IDs, unknown output item types, malformed custom tool input, invalid schemas, and malformed registry/provider contracts, plus config tests proving file/env/CLI source-specific invariants are enforced uniformly. Startup validation tests must prove extension mistakes surface as typed composition failures rather than package initialization panics. Client and CLI tests must prove known errors map to typed errors and unknown internal failures preserve diagnostic text only in the appropriate operator-visible channel.

- [ ] `TD-019` [P0] Runtime, session transcript, and metadata authorities do not share one commit/recovery boundary.

  Runtime and session persistence use several partial commit boundaries. `server/runtime/engine_message_ops.go` mutates `e.chat` before calling `e.store.AppendEvent` in `appendMessage` and `appendMessageWithoutConversationUpdate`; if persistence fails, live runtime state has already changed. `server/runtime/compaction_utils.go` mutates the chat store through `replaceHistory` before appending the `history_replaced` event, then only updates related store metadata such as compaction reminders and usage state after a successful append. `server/session/store.go` names `appendEventsAtomicLocked`, but the implementation performs separate operations: `appendEventsLogLocked` appends to `events.jsonl`, then `s.meta.LastSequence`, `UpdatedAt`, and counters are mutated, then `persistMetaLocked` writes `session.json.tmp` and renames it to `session.json`. If metadata persistence fails after the append, the event log may contain events whose associated metadata fields, first-prompt preview, freshness-derived metadata, or observer snapshot were not durably committed. The post-migration metadata layer adds another authority: `server/metadata/store.go` installs itself as a `session.PersistedSessionResolver`, reconstructs `session.Meta` from SQLite rows in `sessionMetaFromRecordRow`, and upserts `session.PersistedStoreSnapshot` into SQLite in `upsertSessionSnapshot` while large artifacts such as `events.jsonl` remain file-backed under `artifact_relpath`. Startup repairs only part of this split state: `server/session/event_log.go` reconciles `Meta.LastSequence` and conversation freshness from the log in `bootstrapEventLogStateLocked`, but other metadata derived before the append is not rebuilt there. The same persistence layer rewrites `events.jsonl` during periodic compaction through `readEventsFile` and `writeEventsFile`, and `writeEventsFile` replaces the log without fsyncing the file or parent directory.

  A crash, disk error, permission error, failed append, failed SQLite update, failed artifact move, or failed rename can leave live runtime state, durable event rows, file-backed session artifacts, and SQLite-derived metadata in different states. Users can see transient transcript entries, stale compaction projections, missing prompt previews, stale run/session summaries, stale workspace/session targeting, or incomplete cache-warning visibility after restart even though some part of the write path appeared to succeed. Engineers must reason about recovery behavior separately for each live projection, file artifact, SQL row, and derived metadata field instead of relying on one commit boundary.

  Remediation task: define one runtime/session persistence commit contract for transcript mutations, event appends, file-backed artifacts, SQLite metadata rows, and derived metadata. Runtime chat state should be updated only from successful durable commits, or live mutations must have explicit rollback/replay semantics that are covered by tests. Either make the event log the sole durable authority and rebuild all derived metadata/indexes idempotently on startup, or introduce a transactional metadata/index authority that commits event rows, artifact references, and metadata updates together. The implementation must respect existing decisions that normal event writes are append-only and fsync policy is configurable, while making failure semantics explicit for no-fsync modes. Event-log compaction and SQLite snapshot import must use crash-safe replace/update protocols with temp-file cleanup, directory sync where configured, and startup diagnostics for interrupted rewrites or mismatched SQL/file state.

  Regression prevention must include injected-failure and crash-restart tests for failures after live chat mutation but before event append, after event append but before file metadata persistence, after temp metadata write but before rename, after SQLite metadata update but before artifact commit, after artifact commit but before SQLite update, during event-log compaction rewrite, and during observer publication. Tests must prove that live and replayed sessions agree on transcript entries, compaction projections, last sequence, first prompt preview, conversation freshness, workspace/session targeting, run summaries, goal state, and cache-warning visibility, or surface a typed corruption diagnostic. Future persistence code must not use "atomic" naming unless tests prove all live and durable resources in the write are committed, rolled back, or recoverable as one unit.

- [ ] `TD-020` [P1] Background shell output streaming has no bounded chunk or backpressure contract.

  Background shell output is retained in temp log files and streamed through ad hoc subscription reads. `server/tools/shell/manager.go` starts every command with an app-global `Manager`, writes stdout/stderr into `processEntry.logFile`, keeps only a bounded in-memory `pendingOutput`, and emits completion events through one non-blocking `notify` channel. `server/tools/shell/manager_output.go` implements `(*outputSubscription).Next` by opening the process log, seeking to `offset`, and calling `io.ReadAll(file)` for all bytes currently available. `server/transport/gateway.go` forwards each returned `ProcessOutputChunk` as one JSON-RPC notification in `serveProcessOutputSubscription`, and `shared/client/remote_stream.go` unmarshals each notification as one chunk. `shared/serverapi/process_output.go` validates only `process_id` and non-negative `offset_bytes`; there is no negotiated max chunk size, server-side page size, subscriber buffer policy, or slow-subscriber behavior in the contract. `server/tools/shell/manager.go` also creates a temp directory with `os.MkdirTemp("", backgroundLogDirPrefix)`, while `(*Manager).Close` kills processes and waits but does not remove the temp log directory.

  A process that writes a large burst can force one subscription poll to read and marshal a large tail into memory, block a transport send on a huge notification, or make clients process output without a predictable paging budget. Completion notices and inline output use truncation, but the streaming contract itself is unbounded. Temp logs can also accumulate for the lifetime of the process and remain after manager close, increasing disk usage during long-running agent sessions.

  Remediation task: define a bounded background-output stream contract. The process-output API must require a server-enforced maximum bytes/chars per chunk, return `next_offset_bytes`, expose truncation/backpressure metadata, and let clients resume from offsets without ever reading the whole remaining log. The manager should retain log files under an explicit ownership and retention policy, remove or archive its temp directory on close according to that policy, and make completion events use the same bounded reader. Keep the v1 product decision that background process ids are app-global and owner session metadata is advisory, but make output memory/disk lifetime explicit and testable.

  Regression prevention must include large-output tests that subscribe from offset zero and from a tail offset while asserting bounded memory/chunk size, correct offset progression, completion delivery, slow-subscriber behavior, and cleanup of manager temp logs on close. Transport tests must fail if one process-output notification can exceed the declared chunk limit or if inline/completion/streaming output paths use different truncation and offset semantics.

- [ ] `TD-021` [P2] Async and remote tests rely on sleep/poll deadlines instead of deterministic lifecycle signals.

  Timing-based test waits are spread across the repository, especially around remote startup, subscriptions, projected runtime UI, native scrollback, and background process UX. Examples include `cli/app/session_server_target_part3_test.go` helper loops such as `waitForConfiguredRemoteIdentity`, `waitForRemoteTranscriptPage`, `waitForRemoteProcess`, and `waitForRemoteInlineOutput`, which poll until five-second deadlines with `time.Sleep(10 * time.Millisecond)`; `cli/app/session_server_target_test.go` and `cli/app/session_server_target_part2_test.go` reuse the same deadline helpers for multi-server ask/approval/session recovery flows; `cli/app/ui_native_scrollback_integration_part2_test.go` and `cli/app/ui_native_scrollback_integration_part3_test.go` use many fixed sleeps before assertions around window size, startup replay, runtime disconnect, background completion, and deferred final deltas; `shared/client/remote_transport_test.go` polls reconnect state with sleep and uses stalled listener sleeps; `server/sessionruntime/service_test.go` uses timeout windows such as 10 milliseconds to test release cancellation; and `cli/app/ui_path_reference_search_test.go` waits on path-reference async events with artificial loading delays.

  These tests encode scheduler, machine speed, process startup, network, filesystem, and Bubble Tea event-loop timing assumptions. They can fail under load or pass while missing ordering bugs, and slow deadline loops make the suite more expensive. Engineers changing lifecycle code must tune sleeps instead of relying on deterministic readiness, completion, and queue-drain signals.

  Remediation task: replace sleep-driven async tests with deterministic test clocks, explicit lifecycle probes, fake transports/listeners, controllable event loops, and channel acknowledgements owned by the subsystem under test. Startup and remote-server tests should wait on server readiness events rather than polling configured identity. UI/runtime tests should drive Bubble Tea messages and runtime-event bridges with explicit flush/ack hooks. Background process and path-reference tests should expose deterministic started/output/complete signals and fake timers rather than wall-clock sleeps.

  Regression prevention must include a test-harness lint or review check that flags new `time.Sleep`, unbounded polling loops, and arbitrary `time.After` deadlines in tests outside small allowlisted integration cases. Each migrated async subsystem needs harness tests proving readiness, event delivery, cancellation, reconnect, and cleanup can be exercised without real sleeps. Remaining wall-clock integration tests must document why deterministic seams are insufficient and keep timeouts centralized.

- [ ] `TD-022` [P1] `server/session.Store` holds its global mutex across filesystem scans, rewrites, and fsync work.

  `server/session/store.go` uses one `sync.Mutex` to protect metadata, event-log offsets, freshness, and persistence state. Many methods hold that mutex while doing filesystem work. `SetName`, `SetInputDraft`, `SetCompactionSoonReminderIssued`, and `SetWorktreeReminderState` call `persistMetaLocked`, which can `EnsureDurable`, marshal metadata, write `session.json.tmp`, and rename it while the store lock is held. `AppendEvent`, `AppendTurnAtomic`, and `AppendReplayEvents` hold the same lock through `appendEventsAtomicLocked`, which can compact the event log, append to `events.jsonl`, fsync according to policy, mutate metadata, and persist metadata. `ReadEvents` and `WalkEvents` hold the lock while opening and parsing the whole event log; `server/session/event_log.go` performs `readEventsFile`, `writeEventsFile`, `walkEventsFile`, tail repair, and compaction under callers' locked paths.

  The session store is a central runtime persistence boundary, so holding one global lock across disk I/O, full-log scans, compaction rewrites, and fsync can stall unrelated session operations, runtime reads, draft persistence, run metadata, goal updates, fork/replay, and live/dormant projections. Large sessions amplify the problem because the longest operations are unbounded history reads or full rewrites. This also makes lock-order review harder because callers cannot distinguish quick in-memory critical sections from potentially slow filesystem work.

  Remediation task: separate in-memory state protection from persistence I/O. Introduce a small critical section that builds immutable write/read plans, then perform filesystem operations outside the store mutex with explicit version checks or a serialized persistence worker. Event-log scans and transcript projections should use streaming readers that do not block unrelated metadata mutations. Metadata persistence should commit through a narrow write transaction that validates the expected store version before publishing results. Preserve current ordering and crash-recovery semantics while making lock ownership and I/O boundaries explicit.

  Regression prevention must include concurrency tests that run draft persistence, event appends, read-model queries, prompt-history reads, and compaction/fork operations against slow or blocking filesystem fakes and prove unrelated operations are not serialized behind long scans. Add lock-scope tests or static checks that fail when `server/session.Store.mu` is held while calling file open/read/write/rename/fsync or full-history projector functions.

- [ ] `TD-023` [P2] Test seams and startup collaborators are mutable package globals.

  Several command, startup, metadata, and service seams are package-level variables mutated by tests. `cli/kent/main.go` declares `runInteractiveApp` and `runPromptApp`, and `cli/kent/main_test.go` repeatedly replaces them. `cli/kent/service_types.go` declares `runServiceCommand`, `serviceHTTPClient`, `resolveServiceExecutablePath`, `loadServiceSpec`, and `serviceBackendFactory`; tests in `cli/kent/service_command_test.go`, `cli/kent/service_backend_darwin_test.go`, and `cli/kent/service_backend_windows_test.go` mutate these globals. `cli/kent/binding_commands.go` declares mutable command seams such as `bindingCommandRPCTimeout`, `bindingCommandRemoteOpener`, `bindingCommandWorkspaceResolver`, `bindingCommandSessionRetargeter`, and `bindingCommandLocalSessionLifecycleClient`, which are replaced across binding command tests. `cli/kent/serve.go` uses global `startServeServer` and `newServeStartupHandlers`. Server packages also carry global test hooks: `server/metadata/store.go` declares `statPathForAvailability`, `registerWorkspaceBindingAfterLookupMissHook`, `insertWorkspaceBindingAfterProjectUpsertHook`, and `rebindWorkspaceBeforeUpdateHook`, while `server/bootstrap/embedded.go` declares `syncGenerated = generated.Sync`.

  Mutable package globals make tests order-dependent and unsafe to parallelize, and they hide production dependency seams behind process-wide state. A failed test can leave a mutated function for later tests, and concurrent packages cannot safely exercise different startup/metadata behaviors in the same process. Engineers adding new command flows are encouraged to add another global hook instead of introducing explicit composition.

  Remediation task: replace mutable test globals with explicit dependency structs and per-test fixtures. CLI root commands should be constructed with a `CommandDependencies` or runner object containing app runners, service backends, remote openers, timeouts, filesystem/process adapters, and output sinks. Server metadata/startup hooks should become options or interfaces passed to stores/services at construction, with tests building isolated instances. Keep production defaults as immutable constructors, not exported or package-mutable variables.

  Regression prevention must include a static check or architecture test that flags new mutable package-level function variables and test-only global hooks outside an allowlist for true constants/sentinels. Command and server tests should use `t.Parallel` where possible after migration to prove seams are instance-scoped. Any remaining global override must have a `t.Cleanup` helper that serializes access and documents why dependency injection is not feasible.

- [ ] `TD-024` [P1] Client and UI async helpers use ad hoc goroutine lifecycles without owned cancellation and join contracts.

  Several production helpers start goroutines directly and rely on channel closure or incidental transport shutdown instead of one owned lifecycle group. `shared/client/remote_rpc.go` starts `(*remoteControlConn).readLoop` in `newRemoteControlConn`, while `(*remoteControlConn).Close` calls `fail` and closes the underlying connection but has no wait path proving the read loop exited. `shared/rpcwire/websocket.go` starts `(*webSocketConn).startReadLoop`, closes `closed` in `Close`, and closes the `events` channel from the read-loop defer, but `Close` does not join the read loop or expose a completion error. `cli/app/controller_lease.go` starts `(*controllerLeaseManager).runRecovery` with `context.Background()` and a timeout, with no owner close/cancel hook if the UI attachment is torn down. `cli/app/ui_path_reference_search.go` starts `run`, `armLoadingDelay`, `buildCorpus`, and match workers; `Stop` waits only for the main `run` loop, while `buildCorpus` uses a fresh background context and can keep an `rg --files` subprocess alive until its own timeout after the service is stopped. `server/auth/openai_oauth_browser.go` starts an OAuth callback server goroutine and `(*OAuthCallbackListener).Close` shuts the server down but does not wait for the goroutine to report completion.

  Shutdown and reconnect correctness depends on local convention: close a channel, hope blocking calls unblock, and trust no worker will publish after its owner is gone. That increases risk of goroutine leaks, late UI events, subprocesses continuing after UI teardown, flaky tests, and lost root-cause errors during remote disconnects, controller lease recovery, path autocomplete, and OAuth callback handling.

  Remediation task: introduce explicit lifecycle ownership for client and UI async helpers. Each helper that starts goroutines or subprocesses must own a parent context, cancellation function, wait group or errgroup, terminal error publication, and idempotent `Close`/`Stop` that cancels children and waits for them before returning. Remote control and WebSocket connections should expose a closed-and-drained completion path. Controller lease recovery and path-reference search should derive all workers and subprocesses from owner contexts instead of `context.Background()`. OAuth callback listeners should wait for the server goroutine to exit and return or log its terminal error consistently.

  Regression prevention must include lifecycle tests that start each helper, cancel or close it while work is blocked, and assert all owned goroutines/subprocesses exit before `Close`/`Stop` returns. Add leak-oriented tests or a static review guard for production `go` statements requiring an owner context and wait path. Tests must fail when workers can send late events after owner shutdown, when connection `Close` returns before read-loop termination is observable, or when UI/service teardown leaves path-search or lease-recovery work running in the background.

- [ ] `TD-026` [P0] Session repair and discovery paths hide corrupted persistence from operators.

  Session persistence has several silent recovery and skip paths. `server/session/store.go` implements `ListSessions` by continuing when `readRegularSessionFile` fails or `json.Unmarshal` of `session.json` fails, and tests in `server/session/store_part2_test.go` explicitly expect malformed or symlinked session metadata to be skipped from listings. `server/session/event_log.go` treats a malformed final non-newline JSONL record as `droppedTrailingEOF` in `parseEventsFromReader` and `walkEventsFromReader`, then `bootstrapEventLogStateLocked` compacts the log when that flag is set. `repairTrailingLineLocked` truncates invalid tail bytes in `events.jsonl` before appending new events. These paths do not persist a session health marker, recovery event, repair count, truncated-byte count, or operator-visible listing entry for the damaged session.

  Corrupt or partially written sessions can disappear from pickers or lose trailing event bytes without a durable explanation. Users and support tooling cannot tell whether a dormant session is clean, repaired, truncated, skipped due to symlink hardening, or missing metadata. Engineers also lose evidence needed to debug persistence bugs because repair state is not represented as structured metadata.

  Remediation task: add observable persistence health and repair reporting. Session discovery should return damaged-session records with typed reason codes where safe instead of silently omitting them. Event-log repair should persist structured diagnostics for truncated tails, dropped final records, sequence reconciliation, missing metadata, and symlink/security rejection without making corrupted payloads model-visible. Startup, session picker/status surfaces, and logs should expose concise operator-actionable warnings, while low-level store APIs should return typed corruption or repaired-state metadata to callers that need policy decisions.

  Regression prevention must include tests that corrupt `session.json`, replace it with a symlink, truncate `events.jsonl`, append malformed final records, remove metadata while keeping events, and then assert that discovery/startup/read paths produce structured health diagnostics rather than silent skips. Tests must fail if a repair mutates durable state without recording what was changed, or if a damaged session can vanish from all operator-visible recovery surfaces.

- [ ] `TD-027` [P1] Metadata list APIs return unbounded result sets without cursor contracts.

  Project and session metadata listings have no pagination contract. `shared/serverapi/project_view.go` defines an empty `ProjectListRequest` and `SessionListByProjectRequest` with only `ProjectID`. `server/metadata/queries.sql` implements `ListProjects`, `ListProjectWorkspaces`, `ListSessionsByProject`, and `ListSessionsTargetingWorktree` without `LIMIT`, cursor, or page-size arguments; `ListProjects` also aggregates all visible sessions per project with a `GROUP BY`. `server/metadata/store.go` materializes each query into full Go slices, and `server/projectview/service.go` forwards those full lists to clients. Startup planning uses the same unbounded path: `selectSingleAvailableWorkspace` calls `ListProjects` and then `ListProjectWorkspaces` while deciding whether a server-browsing workspace can be auto-selected.

  Large installations with many projects, workspaces, worktrees, or sessions can make startup, binding flows, project pickers, and session pickers perform full-database scans and allocate large response payloads. The API cannot add backpressure or lazy loading without a protocol change, and query performance depends on current data size rather than an explicit budget.

  Remediation task: introduce cursor-based pagination and bounded summaries for metadata list APIs. Project, workspace, session, and worktree listing requests must include limit/cursor fields with server-enforced maximums, deterministic sort keys, and next-cursor responses. Startup paths that need existence or uniqueness decisions should use targeted count/exists queries rather than fetching whole lists. SQL queries must use indexes aligned to their sort/filter keys, and client UI flows must support incremental loading without changing selection semantics for existing users.

  Regression prevention must include large-database tests for project, workspace, session, worktree, binding, and startup-planning queries that assert bounded row counts, stable cursor ordering, index-backed query plans where practical, and correct next-cursor behavior across insertions. Route-contract tests must fail when new list endpoints omit limit/cursor semantics or return unbounded slices.

- [ ] `TD-028` [P1] Tool contracts and transcript formatting still require parallel per-tool edits.

  Tool facts are centralized in part, but not as one complete source of truth. `server/tools/definitions.go` stores tool IDs, aliases, model descriptions, JSON schemas, default-enabled flags, runtime availability, request exposure, presentation kind, render behavior, and formatter callbacks. `shared/toolspec/toolspec.go` separately stores the same tool catalog IDs, default-enabled IDs, parse aliases, config aliases, and config display names. Concrete handlers still parse inputs independently in files such as `server/tools/shell/exec_command_tool.go`, `server/tools/shell/write_stdin_tool.go`, `server/tools/patch/tool.go`, `server/tools/edit/tool.go`, `server/tools/readimage/tool.go`, and `server/tools/askquestion/tool.go`. Transcript formatting is concentrated in `server/tools/transcript_contracts.go`, a 978-line file that contains per-tool call metadata builders, per-tool result formatters, patch rendering, ask-question compatibility output, web-search formatting, shell render-hint detection, JSON fallback formatting, `sedPrintRangePattern`, and command-shape helpers such as `parseCatFileArg`, `parseNlFileArg`, and `parseSedFileArg`.

  Adding or changing a tool still requires synchronized edits across catalog metadata, config aliases/defaults, model schema text, runtime input parsing, result JSON shape, transcript rendering, TUI expectations, and docs. The current structure also pulls per-tool presentation logic away from tool implementations, making it easier for schema descriptions, handler validation, model-facing errors, and transcript renderers to drift.

  Remediation task: make each tool declare one typed contract that owns its ID, aliases, config name, default-enabled state, model schema, input decoder/validator, runtime handler, result type, model-facing error guidance, transcript call metadata, result formatter, render hints, hosted-output decoder, and docs metadata. Assemble global catalogs from those per-tool contracts instead of duplicating IDs and aliases in `shared/toolspec` and `server/tools/definitions.go`. Move per-tool transcript adapters next to tool implementations, and keep shared formatting helpers only for genuinely common JSON/patch/shell primitives. Protocol-affecting schema changes must include migration/release notes for persisted transcripts or config aliases when applicable.

  Regression prevention must include contract tests that fail when a tool has a handler without a definition, a definition without a handler, alias/default drift between config and runtime, schema fields without matching typed input validation, result fields without transcript formatting coverage, or docs generated from stale metadata. Adding a new tool should require one per-tool contract plus focused handler tests, with generated catalog/schema/docs tests proving all model-facing and transcript-facing surfaces are in sync.

- [x] `TD-032` REMOVED

- [ ] `TD-033` [P0] Auth readiness and bootstrap policy is split across startup, health, gateway, bootstrap service, and clients.

  Auth readiness is calculated and acted on in several places. `server/auth/gate.go` defines `EvaluateStartupGate` and `EnsureStartupReady`. `server/authservice/flow.go` loops over `mgr.Load`, `mgr.StoredState`, `EvaluateStartupGate`, `EnsureStartupReady`, env-key detection, and handler interaction. `server/authservice/bootstrap_service.go` stores `authRequired`, implements `authReady`, returns bootstrap status, completes bootstrap modes, and recomputes `EvaluateStartupGate` for responses. `server/transport/gateway.go` has its own `serverAuthReady` and `methodRequiresServerAuth` gates for unary and subscription routes. `server/startup/serve_server.go` has a separate `serverAuthReady` for health/readiness JSON. Client startup repeats bootstrap behavior in `cli/app/remote_auth_bootstrap.go` and local startup wires the flow again through `cli/app/auth_gate.go`, `server/startup/startup.go`, and `server/startup/embedded_server.go`.

  Startup and remote attach can drift in when auth is required, which methods are allowed before readiness, how env API keys win over stored OAuth, which errors are retryable, and what readiness health endpoints report. Security review is harder because provider-auth readiness, interactive auth UX, remote bootstrap capability, route pre-auth policy, and health readiness are not one typed state machine. A future provider/auth mode could be accepted by one path but rejected or silently skipped by another.

  Remediation task: make auth readiness a single server-owned policy service with typed states and transitions. The service must own provider-auth requirement evaluation, stored versus env source preference, OAuth/API-key readiness, bootstrap modes, readiness diagnostics, allowed pre-ready route categories, and health/readiness projection. Startup flows, embedded startup, remote bootstrap clients, gateway route gates, and health handlers should consume this service or generated projections instead of recomputing readiness. Keep UI-specific picker rendering in CLI, but move policy decisions and public error mapping behind one auth readiness contract.

  Regression prevention must include table tests that run the same readiness matrix through local startup, remote bootstrap, gateway unary routes, subscriptions, health, and readiness endpoints. Cases must cover OpenAI default auth required, custom base URL no-auth allowed, explicit first-party ChatGPT/OpenAI hosts, saved OAuth, env API key with each preference, conflicting saved/env sources, skipped auth, refresh failure, and unsupported bootstrap modes. Route-contract tests must fail when pre-ready exposure drifts from the auth service's declared policy.

- [ ] `TD-034` [P0] Outside-workspace approval audit trails are inconsistent across file tools.

  Outside-workspace access goes through shared guard logic but only one tool wires structured audit logging. `server/tools/fsguard/guard.go` can call `onApproved` with reasons such as `temporary_allow`, `configured_allow`, `session_allow`, `call_allow`, `allow_once`, and `allow_session`. `server/tools/readimage/tool.go` exposes `OutsideWorkspaceAuditLogger`, logs approved outside-workspace reads through `logOutsideWorkspaceApproval`, and `server/runtimewire/tool_registry.go` wires that logger to `logger.Logf("tool.view_image.outside_workspace.approved ...")`. In the same registry, patch and edit handlers are built with `patchtool.WithOutsideWorkspaceApprover` and `edittool.WithOutsideWorkspaceApprover` but no audit logger or `onApproved` hook. `server/tools/patch/outside_workspace_guard.go` supports an `onApproved` callback, but `server/tools/patch/tool.go` and `server/tools/edit/tool.go` do not expose options that let runtime wiring record structured audit rows for outside-workspace edits. Denials and approval failures are returned as tool errors, but approved session/configured outside-workspace write permissions are not logged with the same structured path/reason metadata as reads.

  Sensitive filesystem actions become harder to review after the fact. Operators can see some read approvals in run logs but not the matching write approvals, configured outside-workspace write bypasses, or session-wide write allowances. Security and support workflows must reconstruct write permissions from tool calls and ask-question answers instead of a single audit stream, and future file tools can accidentally skip logging by using the shared guard without wiring `onApproved`.

  Remediation task: define one sensitive-action audit contract for outside-workspace reads, outside-workspace writes, configured allows, session allows, denials, approval failures, process kills, and destructive worktree actions. File tools should receive an audit logger through shared guard options, and patch/edit/read-image should emit the same structured fields: tool name, call id where available, owner session id, requested path, resolved path, workspace root, decision, reason, approval commentary presence, and whether the action was read or write. Keep model-visible denial text concise, but persist operator-visible audit rows in run logs or another structured diagnostics sink.

  Regression prevention must include tests that approve and deny outside-workspace `patch`, `edit`, and `view_image` calls through allow-once, allow-session, configured allow, temporary allow, and normal denial paths, then assert one structured audit row is emitted with normalized paths and decision metadata. Add an architecture test that fails when a filesystem guard is constructed for a sensitive tool without an audit callback, and tests for process kill and destructive worktree operations once they adopt the same audit contract.

- [ ] `TD-035` [P2] Large scenario tests and missing compatibility matrices hide behavioral boundaries and slow focused feedback.

  The test suite contains 314 `*_test.go` files, and 65 of them are at least 800 LoC, totaling 61,869 LoC in those large files alone. Very large examples include `shared/config/config_part2_test.go` at 1503 LoC, `cli/app/ui_runtime_client_test.go` at 1493 LoC, `server/runtime/engine_part13_test.go` at 1374 LoC, `server/launch/planner_test.go` at 1292 LoC, `cli/app/ui_part4_test.go` at 1168 LoC, `cli/app/ui_scroll_keys_test.go` at 1105 LoC, `cli/app/run_prompt_test.go` at 1099 LoC, `server/sessionruntime/service_test.go` at 1080 LoC, `server/tools/shell/tool_test.go` at 1060 LoC, and `server/transport/gateway_test.go` at 1006 LoC. Many suites are numbered `partN` files, such as `server/runtime/engine_part*.go`, `cli/app/ui_part*.go`, and `cli/app/session_server_target_part*.go`, which groups scenarios by overflow file rather than by subsystem contract. Dependency compatibility has the same boundary problem: `go.mod`, `scripts/ci-check.sh`, and `.github/workflows/ci.yml` rely on normal test/build coverage and do not define a dependency-update compatibility matrix for transport, runtime, persistence, config, auth, docs generation, or supported platforms.

  Test changes require scanning broad files to find fixture setup, expected behavior, helper coupling, and scenario ownership. Large files and absent compatibility matrices make it easier to add another case to an already-heavy integration harness instead of extracting a deterministic unit seam or dependency-contract suite. This slows regular development feedback, increases merge-conflict risk, and makes agents more likely to miss related scenarios or duplicate helpers when adding regression coverage.

  Remediation task: reorganize large tests and dependency compatibility coverage around behavioral contracts and reusable fixtures. Split numbered and oversized test files by domain scenario, such as runtime lifecycle, compaction, reviewer, goal, transcript paging, config inheritance, auth startup, shell process output, UI input/render reducers, and dependency compatibility contracts. Extract common harness builders into small test-only helper packages or focused `*_test.go` helpers with explicit ownership. Keep integration coverage, but prefer unit and contract suites where one subsystem boundary is being tested. Document when a file is intentionally broad and what behavior belongs there. Add an explicit dependency-update compatibility matrix that identifies which tests must exercise upgraded protocol, websocket, SQLite/sqlc, TUI, config, OAuth/auth, archive/install, and docs-generation dependencies.

  Regression prevention must include a test-size or review guard that flags new `*_test.go` files above the repository threshold unless they carry an explicit ownership rationale. Add package-level test indexes or helper docs for the largest domains so new regression tests have an obvious destination. CI should still run the same behavior, but focused package/test names and compatibility-matrix jobs should let engineers run a narrow contract suite without discovering which `partN` file contains the scenario.

- [x] `TD-036` REMOVED

- [ ] `TD-037` [P1] Release, generated-code, and artifact freshness contracts are duplicated or unenforced across scripts, CI, installers, and Homebrew plumbing.

  Release and generated-contract facts are spread across multiple files. `scripts/release-artifacts.sh` declares the supported targets in `release_targets` as `darwin/arm64`, `linux/amd64`, `linux/arm64`, `windows/amd64`, and `windows/arm64`, derives archive extensions, names assets as `kent_${version}_${goos}_${goarch}`, generates `checksums.txt`, verifies Linux static linking, and smoke-tests archives. `.github/workflows/release.yml` repeats the same OS/arch/archive matrix for smoke tests and separately wires Windows installer smoke tests. `.github/workflows/ci.yml` hand-builds a mocked Windows archive and checksum manifest instead of invoking the same release-artifact builder path. `scripts/install.sh` and `scripts/install.ps1` each reimplement OS/arch normalization, release tag normalization, asset-name construction, checksum lookup, archive extraction, and binary naming. `scripts/build.sh`, `scripts/ci-check.sh`, `.github/workflows/release.yml`, and `scripts/update-brew-tap.sh` each contain separate version-resolution logic, while `scripts/update-brew-tap.sh` builds from the GitHub source tarball rather than the binary artifact matrix. The release workflow also auto-bumps `VERSION` when a published tag already exists, but `docs/dev/release.md` describes the workflow as creating the tag from `VERSION` and does not document that patch-bump/rewrite policy. Generated SQLite code has the same missing freshness gate: `server/metadata/sqlitegen/queries.sql.go` is generated from `server/metadata/queries.sql`, but CI does not regenerate it and fail on drift. Config/default/docs artifacts have similar registry-derived data in `shared/config/config_registry.go` without one generated-output freshness contract.

  Adding or removing a release target, changing an asset name, changing version normalization, tightening checksum policy, changing SQL queries, or changing generated config/docs contracts requires synchronized edits across shell, PowerShell, workflow YAML, Homebrew formula generation, generated Go, and docs snapshots. A drift can produce a published artifact that install scripts cannot find, a CI mock that passes while release packaging fails, a Homebrew formula that embeds different build/version assumptions than the official binary release path, or generated SQLite/config/docs code that no longer matches its source contract.

  Remediation task: introduce one release/generated-contract manifest that owns supported targets, archive extensions, binary names, artifact names, checksum policy, installer lookup names, smoke-test matrix, Homebrew build metadata, generated-code commands, and generated docs/config outputs. `scripts/release-artifacts.sh`, installers, workflow matrices, CI mocks, tap update logic, sqlc generation checks, and docs/config generation checks should consume that manifest or generated artifacts instead of rebuilding the matrix in each language. Centralize version normalization, generation commands, and validation in shared script/library code used by build, release, CI, installers, tap updates, and generated-code freshness checks. Keep platform-specific install mechanics separate, but make artifact identity and supported-target policy single-source.

  Regression prevention must include CI tests that compare the release manifest against workflow matrices, install-script asset construction, release-artifact output, and Homebrew formula generation. Add freshness checks that regenerate sqlc output from `server/metadata/queries.sql`, regenerate any config/docs contract artifacts, and fail on dirty diffs to `server/metadata/sqlitegen/queries.sql.go` or generated docs snapshots. Add a dry-run release job or script mode that builds the artifact plan once and asserts installers can resolve every target from the plan. Future target additions or generated contract changes must fail CI unless the manifest, smoke tests, installers, tap formula generation, and generated outputs all consume the new source consistently.

- [ ] `TD-039` [P1] Confirmed workflow schema-minimization decisions are not implemented.

  Current metadata schema and queries still store `projects.next_task_seq`, `task_runs.metadata_json`, `task_runs.workflow_revision_seen`, and transition-edge display/config snapshot data. The product decisions are now locked in `docs/dev/specs/workflow-orchestration.md`: keep `tasks.short_id`, remove stored `projects.next_task_seq`, remove `task_runs.metadata_json`, derive run revision from run-start snapshots after migration, keep `task_comments.author_id`, remove transition-edge display/config snapshots after run-start snapshots become the sole historical source, and keep `sessions.first_prompt_preview` plus `sessions.input_draft`.

  The schema now carries redundant or unclear workflow state that future migrations can accidentally preserve as product truth. Keeping these fields after the decisions are locked increases API/schema drift risk and makes workflow history authority harder to reason about.

  Remediation task: implement a schema cleanup pass for the confirmed removals/derivations. First slice: add compatibility/read-model tests that prove stable task short IDs, run revision display, and transition display/config still render from run-start snapshots, then remove `task_runs.metadata_json` behind those tests. Second slice: replace `projects.next_task_seq` with transactional sequence allocation and concurrency coverage. Third slice: migrate and derive `task_runs.workflow_revision_seen`, then remove transition-edge display/config snapshots once run-start snapshots cover historical display/config needs. Do not remove `tasks.short_id`, `task_comments.author_id`, `sessions.first_prompt_preview`, or `sessions.input_draft`.

  Regression prevention must include migration tests proving existing databases keep stable task short IDs and historical run/transition display, schema tests rejecting reintroduced removed columns, workflow store tests for concurrent task sequence allocation, and read-model tests proving run revision and transition display/config still render from the new authorities.

- [ ] `TD-040` [P2] `sessions.launch_visible` has unclear product semantics.

  `sessions.launch_visible` is stored in metadata migrations and used by list queries to decide which sessions appear in project/session surfaces. During review, the field had no clear product meaning for User beyond the current implementation detail, so it should not be promoted as a product decision without a better domain name and ownership contract.

  An opaque sticky visibility cache can become accidental product truth. If future session listing, launch, archival, or cleanup behavior depends on it, engineers will not know whether they are preserving UX state, hiding technical sessions, or caching a derivable read-model fact.

  Remediation task: define session launch/listing visibility semantics explicitly. Either rename and keep the stored field with documented invariants, derive visibility from durable session facts, or replace it with a clearer lifecycle/read-model field. Update specs only after the behavior is intentionally chosen.

  Regression prevention must include project/session listing tests for interactive launches, headless runs, imported/legacy sessions, hidden/internal sessions, and migration cases. Tests should fail if a session disappears from operator-visible recovery surfaces without a typed visibility reason.

- [ ] `TD-041` [P2] Desktop GUI DTO drift checks are representative fixtures, not an authoritative Go-to-TypeScript contract.

  Desktop API code validates responses with hand-written Zod schemas in `apps/desktop/src/api/schemas/*` and has representative contract tests in `apps/desktop/src/api/client.test.ts`. Go route and client contract tests cover internal RPC mappings, but there is no generated or authoritative cross-language fixture/freshness check proving Go DTO changes and TypeScript schemas stay aligned for every GUI-used method.

  A server DTO change can pass Go route tests while silently drifting from the desktop adapter until a representative fixture happens to cover that field. This weakens the GUI remote-control boundary and increases the chance of runtime `ContractError` failures after backend workflow/API changes.

  Remediation task: add an authoritative Go-to-TypeScript GUI contract drift check. Options include generated JSON fixtures from Go DTO examples consumed by Vitest, generated schemas from the shared route contract, or a small contract manifest that enumerates GUI-used methods and validates request/response shapes in both languages. Keep GUI feature components consuming adapted view models rather than raw DTOs.

  Regression prevention must include CI freshness checks that fail on dirty generated fixtures/schemas, coverage for every GUI-used RPC method, negative tests for missing/extra/renamed fields, and architecture lint preserving the adapter boundary.

- [ ] `TD-042` [P1] Workflow subsystem files concentrate graph, lifecycle, view, and DTO responsibilities.

  The workflow stack carries several oversized multi-responsibility files. `server/workflowview/service.go` is 2216 LoC and exposes nine projection methods on one `Service` covering definition reading, board rendering, attention list assembly, transcript pagination, and run-state projection. `shared/serverapi/workflow.go` is 1916 LoC and defines 147 structs with 48 `Validate() error` methods in one package, mixing wire DTOs, request validation, and workflow domain entities such as nodes, edges, and transition groups. `server/workflowstore/store.go` is 1231 LoC and guards graph mutations (`AddNode`, `UpdateNode`, `AddNodeGroup`, `AddEdge`), run/task lifecycle, and event publishing (`PublishWorkflowEvent`) behind one `eventMu sync.RWMutex`. `server/workflowsvc/service.go` is 1278 LoC and threads collaborators `taskWorktreeEnsurer`, `taskRuntimeCanceler`, `schedulerNotifier`, `pendingPromptResponder`, and `transitionApprover` through one request-routing service. `server/workflowrunner/starter.go` is 1025 LoC and owns per-run goroutine lifecycle through a cancel map, task map, and wait group.

  Workflow changes require synchronized edits across DTO/validation, graph store, lifecycle store, view projections, and run execution, and the single-file concentration makes graph mutation, run lifecycle, and read-model rendering hard to test or reason about independently. Wire types, validation, and domain entities sharing one package also blurs the transport boundary the rest of the server keeps separate.

  Remediation task: decompose the workflow subsystem along its existing seams. Split `serverapi/workflow.go` into wire request/response types, request validation, and a domain entity package so transport types stop carrying domain modeling. Split `workflowstore/store.go` into a graph store (node/edge/group mutations) and a run store (run/task/event lifecycle) with explicit ownership of event publishing. Split `workflowsvc/service.go` into CRUD, run execution, and attention/approval services consuming narrow collaborator interfaces. Split `workflowview/service.go` into definition, board, attention, and transcript projection services. Keep `workflowrunner` goroutine ownership behind one run-lifecycle coordinator.

  Regression prevention must include behavior-preserving tests for graph mutation, run/task lifecycle, attention assembly, transcript pagination, and run-state projection that survive the split unchanged, plus a size/coupling guard for the workflow packages. Validation tests must prove request invariants are enforced after wire/domain separation, and import-boundary checks must fail if wire DTO packages re-absorb domain entities or view projection logic.

- [ ] `TD-043` [P1] `server/metadata/store.go` mixes workspace, worktree, session, project, and sequence domains in one store.

  `server/metadata/store.go` is 2283 LoC and the largest production file. One `Store` exposes 51 methods spanning workspace binding (`EnsureWorkspaceBinding`, `ResolveWorkspacePath`, `RegisterWorkspaceBinding`, `RebindWorkspace`), worktree records (`UpsertWorktreeRecord`, `GetWorktreeRecordByID`, `DeleteWorktreeRecordByID`, `ListWorktreeRecordsByWorkspaceID`), session execution targeting (`UpdateSessionExecutionTargetByID`, `RetargetSessionWorkspace`, `DeleteSessionRecordByID`), project lifecycle (`CreateProjectForWorkspace`, `DeleteProject`, `UnlinkProjectWorkspace`, `AttachWorkspaceToProject`), task sequence allocation (`AllocateProjectTaskSequence`), and project read-model listings (`ListProjects`, `ListProjectWorkspaces`, `GetProjectOverview`). Helper closures and binding-recovery logic such as `registerWorkspaceBindingConverged` and `recoverWorkspaceBindingAfterCanonicalRootConflict` share the same file.

  Every metadata change touches one dense file that owns unrelated transactional concerns, so workspace binding, worktree records, session targeting, project lifecycle, and read-model assembly cannot be reviewed, tested, or evolved in isolation. Adding a new metadata domain extends an already-overloaded store rather than a focused boundary.

  Remediation task: split the store into domain-scoped files or sub-stores for workspace binding, worktree records, session execution targets, project lifecycle, and sequence allocation, keeping only `Open`/`Close`/`DB`/`Queries` accessors and shared transaction wiring in the root store as thin routing. Preserve current transaction boundaries and binding-recovery semantics during extraction.

  Regression prevention must include behavior-preserving tests for binding resolution, worktree record CRUD, session retargeting, project create/delete/unlink/rebind, and task sequence allocation that pass unchanged after the split, plus a size/coupling guard for `server/metadata`. Tests must fail if a domain operation regresses its transaction or recovery behavior during decomposition.

- [ ] `TD-044` [P1] `server/worktree/service.go` concentrates git, locking, targeting, and lifecycle responsibilities.

  `server/worktree/service.go` is 1770 LoC. One `Service` owns a `GitInspector`, two mutexes (`workspaceMu sync.Mutex` and `mu sync.Mutex`), git worktree operations through `s.git.ResolveCreateTarget`/`Add`/`Prune`/`Remove`/`DirtyFileCount`/`deleteBranch`, execution-target syncing, and worktree creation/removal/deletability through `EnsureTaskWorktree`, `DeleteTaskWorktree`, and `EnsureTaskWorktreeDeletable`. Git inspection, workspace mutation locking, target synchronization, and lifecycle management are interleaved in one type.

  The service is a central worktree authority, so combining git subprocess behavior, coarse workspace locking, and lifecycle orchestration in one file makes lock scope and failure handling hard to reason about and raises the risk that a git or locking change silently affects unrelated lifecycle paths.

  Remediation task: separate git inspection, workspace-mutation locking, execution-target synchronization, and worktree lifecycle into focused collaborators with explicit lock ownership, keeping the public service as orchestration over those components. Preserve current creation/removal semantics and locking order during extraction.

  Regression prevention must include behavior-preserving tests for worktree create, remove, deletability, and target sync against fake git runners, plus concurrency tests proving lock scope does not serialize unrelated operations. A size/coupling guard must flag regrowth of the service file.

- [ ] `TD-045` [P1] `server/sessionruntime/service.go` guards multiple lifecycles under one mutex and mixes activation concerns.

  `server/sessionruntime/service.go` is 1461 LoC. One `mu sync.Mutex` guards `handles`, `idleTimers`, `runningTimers`, and `recoveredWarning`, even though these have different lifetimes: handles live for the runtime session, idle timers for seconds, and the recovered warning is set once. `runtimeHandle` additionally owns `controllerLeaseID` and a `*runtimeTakeover`, and `ActivateSessionRuntime` interleaves existing-activation claim, primary-run lease acquisition (`acquirePrimaryRunLease`), new-activation claim, takeover semantics, and best-effort lease release in one method.

  Holding one lock across runtime handle lifecycle, idle-timer churn, lease tracking, and startup warning state makes lock ownership opaque and couples unrelated control flows, so activation, takeover, and idle-unload behavior are hard to test or evolve without risking cross-flow regressions.

  Remediation task: split runtime handle lifecycle, lease tracking, takeover semantics, and idle-timer management into focused components with their own synchronization, and decompose `ActivateSessionRuntime` into claim/lease/takeover stages with explicit ownership. Preserve current activation, takeover, and lease-release semantics during extraction.

  Regression prevention must include tests for activation, takeover, lease release, and idle unload under concurrency that prove finer-grained synchronization preserves behavior, plus lock-scope or size guards that fail if one mutex again guards unrelated lifecycles.

- [ ] `TD-046` [P1] Compaction combines threshold policy, token counting, and request execution in one engine subsystem.

  `server/runtime/compaction.go` (772 LoC) and `server/runtime/compaction_utils.go` (264 LoC) attach compaction policy and execution directly to `Engine`. The same subsystem owns threshold policy (`shouldAutoCompactWithContext`, `ShouldCompactBeforeUserMessage`, `autoCompactPrecisionMarginForLimit`), token counting and caching (`currentInputTokensPrecisely`, `requestInputTokensPreciselyTracked`, `estimateTokensFromBytes`, `storePreciseTokenCount`), provider capability checks (`providerCapabilities`, `preciseInputTokenCountSupported`), request construction (`buildRequestWithoutPromptRefresh`), and remote-output handling (`sanitizeRemoteCompactionOutput`).

  Compaction policy cannot be tested without model execution because thresholds, token estimation, provider capability checks, and request assembly all live on the engine. This couples policy decisions to live model calls and makes it harder to evolve compaction triggering independently of execution.

  Remediation task: introduce an explicit compaction planner/executor boundary so threshold policy can be exercised without model execution, and move token estimation, provider-capability resolution, and request construction into separate components consumed by the planner. Keep the existing steer/queue compaction integration (see TD-019) and persisted status emission intact.

  Regression prevention must include planner tests that exercise threshold and pre-message decisions with fake token counts and provider capabilities, plus executor tests for request construction and remote-output handling, proving policy and execution can be tested in isolation.

- [ ] `TD-047` [P1] Reviewer pipeline lives in engine helpers and classifies responses by message content.

  `server/runtime/engine_reviewer_helpers.go` (445 LoC) owns reviewer request building, meta-context classification, transcript formatting, and response parsing on `Engine`. It classifies and formats responses from content rather than typed protocol: `sanitizeReviewerSuggestions` matches `reviewerNoopToken` via `strings.EqualFold`, `parseReviewerSuggestionsObject` parses suggestion text, `reviewerStatusText` builds operator-facing status from literal phrases such as "Supervisor ran:", and `splitMetaContextItems`/`buildReviewerRequestItemsWithBuilder` reshape transcript items for the reviewer pass.

  Concentrating reviewer orchestration, classification, and formatting in one engine helper makes the reviewer pipeline hard to test in isolation, and content-based classification violates the repository rule against deriving information from substring/string matching, so reviewer protocol changes risk silent misclassification.

  Remediation task: extract reviewer logic into a `server/runtime/reviewer/` subpackage with a typed pipeline: orchestration, request/response formatting, meta-context classification, and transcript formatting as separate units. Replace content-based suggestion and status detection with a typed reviewer protocol response, coordinating with the legacy-shim sunset in TD-014 so no permanent string-based fallback remains.

  Regression prevention must include unit tests for the reviewer pipeline stages and typed protocol parsing that do not depend on literal phrasing, and a guard that fails if reviewer classification reintroduces substring-based detection of suggestions, no-op tokens, or status.

## P2 Findings

- [ ] `TD-007` [P2] `shared/config/config_registry.go` centralizes too many setting responsibilities.

  The file is 1453 LoC. `newSettingsRegistry` declares settings for model, thinking, provider capabilities, theme, server host/port, web search, tools, skills, reviewer, shell, worktrees, and other domains. The same file defines registry interfaces/types, default payload rendering, file-key tree behavior, source tracking, file/env/CLI application hooks, validators, docs/default config rendering helpers, and setting-specific closures.

  Adding or changing one setting requires understanding a dense closure DSL and editing a monolithic literal. Model/provider, TUI, runtime, tool, persistence, and docs generation concerns share one change surface.

  Remediation task: modularize configuration declarations into domain-scoped setting specs while preserving one authoritative load/apply/validate pipeline. Split setting declarations by domain: model/provider, reviewer, runtime/compaction, tools/skills/subagents, TUI/theme, server/transport, auth, worktrees, shell/process output, persistence, and diagnostics. Each setting spec must declare key path, default, TOML/env/CLI decoding, normalization, source reporting, generated default rendering, docs/default-comment behavior, validation, and migration notes in one place. The central registry should assemble domain specs and enforce global invariants, not contain every setting literal.

  Regression prevention must include golden tests proving generated default config output, source attribution, unknown-key validation, env/TOML/CLI precedence, relative path resolution, reviewer inheritance, provider capability overrides, and validation errors are unchanged during extraction. Add a test that every documented setting is registered exactly once and every registered setting has docs/default rendering policy. Future settings should be added by creating/updating a domain spec, with registry-level tests catching missing source, docs, validation, or default behavior.

- [ ] `TD-008` [P2] Large production files violate repository maintainability rule.

  Repository guidance says avoid files over 600 LoC. The original exhaustive production scan found 34 files over 600 LoC: `cli/app/ui_worktrees.go` at 1507 LoC, `shared/config/config_registry.go` at 1453 LoC, `cli/tui/model.go` at 1415 LoC, `server/metadata/store.go` at 1403 LoC, `server/transport/gateway.go` at 1293 LoC, `server/runtime/compaction.go` at 1270 LoC, `cli/app/ui_status.go` at 1266 LoC, `server/metadata/sqlitegen/queries.sql.go` at 1223 LoC, `server/worktree/service.go` at 1178 LoC, `cli/app/ui.go` at 1085 LoC, `server/session/store.go` at 1044 LoC, `cli/app/onboarding_imports.go` at 979 LoC, `server/tools/transcript_contracts.go` at 978 LoC, `cli/app/project_binding_flow.go` at 974 LoC, `cli/app/onboarding_model.go` at 954 LoC, `server/registry/runtime_registry.go` at 913 LoC, `server/runtime/compaction_utils.go` at 865 LoC, `cli/app/ui_native_history.go` at 848 LoC, `server/sessionruntime/service.go` at 823 LoC, `cli/app/ui_runtime_client.go` at 817 LoC, `cli/app/onboarding_flow.go` at 802 LoC, `cli/tui/model_rendering_entries.go` at 784 LoC, `server/core/core.go` at 762 LoC, `server/runtime/engine_state.go` at 720 LoC, `server/llm/openai_http_stream.go` at 707 LoC, `server/runtime/engine.go` at 698 LoC, `server/runtimecontrol/service.go` at 686 LoC, `server/runtime/chat_store.go` at 650 LoC, `cli/tui/model_rendering.go` at 640 LoC, `server/llm/types.go` at 638 LoC, `cli/app/ui_layout_rendering_status_overlay.go` at 637 LoC, `cli/app/ui_processes.go` at 623 LoC, `cli/tui/model_reducer.go` at 611 LoC, `server/tools/shell/postprocess/file_read_context.go` at 603 LoC. TD-003 remediation has since reduced several `cli/app` entries; see TD-003 current evidence above for current app-file sizes.

  These files concentrate multiple responsibilities and make reviews, refactors, and agent-driven changes riskier.

  Remediation task: reduce large production files through responsibility-based extraction, not cosmetic splitting. For each file over 600 LoC, identify cohesive responsibility clusters, current invariants, public/private APIs, tests that protect behavior, and safe extraction order. Extract stable domain seams into smaller files/packages only when the new boundary has a clear owner, narrow API, and regression coverage. Prioritize files that are both large and architecturally central: `cli/app/ui_worktrees.go`, `shared/config/config_registry.go`, `cli/tui/model.go`, `server/metadata/store.go`, `server/runtime/compaction.go`, `cli/app/ui_status.go`, `server/worktree/service.go`, `server/transport/gateway.go`, `cli/app/ui.go`, and `server/session/store.go`.

  Regression prevention must include an automated size/coupling check or documented CI guard that flags new production files over the repository threshold unless explicitly justified. Each extraction should leave behavior-preserving tests in place before moving code, and the catalog should track the responsibility cluster removed, new owner, compatibility risk, and remaining clusters. The goal is lower coupling and clearer ownership; line count reduction alone is not sufficient.

- [ ] `TD-029` [P2] Status overlay mixes UI presentation with network, auth, git, filesystem, and token collection.

  `cli/app/internal/status` owns status data acquisition and imports `net/http`, `os`, `os/exec`, `path/filepath`, `core/prompts`, `server/auth`, `server/runtime`, `shared/client`, `shared/config`, `shared/serverapi`, and `shared/textutil`. The package defines status data structures, collector interfaces, default collector implementations, HTTP subscription usage fetching through `FetchUsagePayload`, OAuth/auth-state refresh handling, `git status --porcelain=v2 --branch` execution, filesystem traversal for Git root discovery, skill and AGENTS inspection through `runtime.InspectSkills`, token estimates by `os.ReadFile`, and request-time context timeouts. Tests exercise package-level collectors in `cli/app/internal/status/*_test.go`.

  A visual overlay owns slow and failure-prone side effects that belong behind application services. Rendering/status changes can accidentally affect auth refresh, quota network calls, Git subprocess behavior, filesystem scans, and token estimation. The package-global fetcher also creates another parallel-test seam and encourages additional UI-local collectors instead of a reusable status domain.

  Remediation task: split status data acquisition from status presentation. Move auth/quota collection, Git inspection, skill/AGENTS scanning, token estimation, config/source summarization, and update/session enrichment into a non-UI status service with typed collectors and injectable filesystem/network/process/auth clients. Keep Bubble Tea code responsible for refresh orchestration, cache consumption, section loading state, and rendering. Replace `statusUsagePayloadFetcher` with an instance-scoped dependency, and make quota/auth behavior respect the existing auth/provider decisions without UI-owned HTTP policy.

  Regression prevention must include unit tests for each collector with fake auth stores, HTTP clients, Git runners, filesystems, and clocks, plus UI tests that assert loading/error rendering without real side effects. Import-boundary checks must fail when UI rendering files import server auth/runtime or spawn subprocesses directly. Parallel tests should prove status collectors are instance-scoped and do not mutate package globals.

- [ ] `TD-030` [P2] Project and workspace picker flows duplicate picker mechanics behind mutable globals.

  `cli/app/project_binding_flow.go` is 974 LoC and defines four package-global runner seams: `runProjectBindingPickerFlow`, `runServerProjectPickerFlow`, `runProjectWorkspacePickerFlow`, and `runProjectNamePromptFlow`. The file contains separate Bubble Tea models for project binding, server project selection, workspace selection, and project naming, with repeated cursor movement, offset/visibility math, markdown header rendering, row/group rendering, theme style setup, key handling, mouse-wheel handling, and cancel/quit behavior. The same package also contains separate session, slash-command, path-reference, auth, onboarding, and worktree picker/list implementations with their own row rendering and key handling.

  Every new picker or overlay can re-implement subtly different navigation, resize, cancel, row visibility, mouse, theme, and markdown behavior. Tests must hook process-global runner variables, and UX fixes such as scroll anchoring, `q`/Esc behavior, no-color/width handling, and selection presentation need to be repeated across many models.

  Remediation task: introduce a reusable alt-screen picker/list framework for CLI startup and overlay surfaces. The framework should own cursor/offset math, resize behavior, key and mouse handling, header/body/footer layout, cancel semantics, row virtualization, filtering where needed, and theme/no-color rendering hooks. Product-specific flows should provide typed rows, row presenters, grouping policy, empty-state copy, and result mapping. Replace package-global runner seams with injected picker runners or small flow dependencies.

  Regression prevention must include shared picker behavior tests for keyboard and mouse navigation, resize, filtering, grouping, cancellation, empty lists, markdown header fallback, terminal width truncation, and no-color output. Flow-specific tests should assert only project/workspace/session/auth semantics, not duplicated picker mechanics. Static checks should prevent new mutable package-level picker runner globals outside the injected flow boundary.

- [ ] `TD-031` [P2] Root CLI command handling combines parsing, usage text, runners, IO, and signal policy.

  `cli/kent/main.go` is 557 LoC and owns root dispatch, `flag.FlagSet` construction, common flag registration, explicit flag tracking, terminal gating, session-id resolution, headless JSON/text output selection, run result formatting, direct `os.Stdout`/`os.Stderr` writes, `signal.NotifyContext` setup for interactive and headless modes, and package-global app runner seams `runInteractiveApp` and `runPromptApp`. `cli/kent/help.go` separately hardcodes usage strings for root, run, session-id, goal, project, attach, rebind, serve, and service commands, while command behavior is also spread across `binding_commands.go`, `goal_command.go`, `serve.go`, `service_command.go`, and `service_types.go` with additional mutable package globals already cataloged in `TD-023`.

  CLI behavior is difficult to extend consistently. Adding or changing a command can require edits across parsing, usage text, JSON error shape, output streams, signal handling, app runner seams, and per-command files. Help text, tests, and behavior can drift because there is no typed command spec tying flags, usage, handler, output mode, and runner dependencies together.

  Remediation task: replace root command procedural dispatch with a declarative command registry. Each command spec should define name, aliases, flags, hidden flags, usage/help text, output modes, environment/session requirements, signal policy, and handler dependencies. Handlers should receive injected runners, IO streams, clock/env adapters, and remote openers through dependency structs rather than package globals. Generate or derive help text and CLI docs from the command specs, and keep headless JSON error contracts centralized.

  Regression prevention must include command registry tests proving every registered command has help text, flag definitions, JSON/text error mapping, and dependency injection coverage. Golden tests must compare generated help and public docs for root/run/goal/project/attach/rebind/serve/service commands. Static checks should fail on new command runner package globals and on command files that write directly to `os.Stdout`/`os.Stderr` outside the command IO adapter.

- [ ] `TD-038` [P2] Desktop native dialog windows are not proven to use OS modal or utility-window roles.

  Desktop modal destinations currently use Tauri `WebviewWindow` creation from `apps/desktop/packages/native-bridge/src/dialogs.ts`. The bridge sets a parent window, centers the child over the parent, hides the window until positioned, then calls `show`, `setAlwaysOnTop(true)`, and `setFocus`. It also sets `skipTaskbar`, `preventOverflow`, overlay title bar, transparency, and window effects. Tauri documents `parent` as owned/transient/child behavior on Windows/Linux/macOS, but the JavaScript `WebviewWindow` options used here do not expose an AppKit `NSPanel`, utility-window, modal-subrole, or window-type setting. Yabai can therefore still see these dialogs as regular windows even though they are intended as hovering modal content.

  Operators using tiling window managers can see native dialog child windows get tiled, moved, focused unexpectedly, or handled by window-manager rules as normal app windows. This is especially visible for Create Task and project creation dialogs because they are opened as native child windows over the board. The current behavior is functional but relies on a combination of parent, always-on-top, skip-taskbar, and focus calls rather than one OS-level modal/panel contract.

  Remediation task: verify the actual macOS window class, role/subrole, level, collection behavior, and yabai-visible attributes for `/native-dialog/*` windows. If they are regular `NSWindow`s, add a macOS-specific native bridge that marks dialog windows as utility/floating panels or modal child windows at AppKit level while preserving Tauri WebView content. The implementation should keep cross-platform behavior explicit: use AppKit-specific panel semantics on macOS, owned/transient windows on Windows/Linux, and a clear fallback when a platform cannot provide real modal/panel classification.

  Regression prevention must include native-window contract checks where feasible: assert dialog options set parent/skip-taskbar/focus behavior, add macOS smoke or integration diagnostics for window level/subrole, and document the yabai rule expectations if native AppKit classification is insufficient. Future native dialogs must use the shared native-dialog bridge rather than creating feature-owned `WebviewWindow`s or in-page modal fallbacks.
