Skip to content

Consolidate debug infrastructure into one home, remove ad-hoc console.* scatter, enforce a typed logger boundary #102

Closed as not planned

Description

@Juliusolsson05

What happened

The app's debug infrastructure has been added to incrementally for months and is now scattered across eight+ directories without a single source of truth. There are 30 files outside those debug homes that contain ad-hoc `console.log` / `console.warn` calls. The result is that there is no canonical answer to "where does debug code live, and how do you add a new debug surface?" — every author makes a local decision, and the decisions disagree.

Current scatter (concrete inventory)

Eight directories that all hold debug-related code:

  • `src/main/performance/` — PerformanceService, LocalJsonlSpanExporter, heapWatchdog, instrumentIpc, ProcessTelemetry
  • `src/shared/performance/` — shared types
  • `src/renderer/src/performance/` — renderer-side perf client
  • `src/renderer/src/features/system-perf/` — the header badge + popover
  • `src/renderer/src/features/performance/` — `PerformancePanel` (per-pane CPU/mem)
  • `src/renderer/src/features/debug/` — DebugPanel, ProxyDebugPanel, FeedDebugPanel, HtmlDebugPanel, AgentInlineTerminal, saveDebugBundle, renderTrace
  • `src/shared/work-context/debug.ts` — `summarizeWorktreeActivity` (debug-only)
  • `src/renderer/src/workspace/runtime/feedDebug.ts` + `hook/persistence/useFeedDebugPersist.ts`

Plus loose files scattered in non-debug directories:

  • `src/main/pasteDebugJournal.ts`, `src/main/ghostJournal.ts`, `src/main/dictationJournal.ts`
  • `src/main/storage/debugRetention.ts`, `src/main/storage/feedDebugLog.ts`, `src/main/storage/debugBundle.ts`
  • `src/main/ipc/debug.ts`, `src/main/ipc/pasteDebug.ts`
  • `src/preload/api/debug.ts`, `src/preload/api/pasteDebug.ts`, `src/preload/api/dictationDebug.ts`

Console-log scatter: 30 source files outside the debug homes carry raw `console.log` / `console.warn` calls. Sample sites (all should route through a typed logger instead):

  • `src/renderer/src/app/App.tsx`
  • `src/renderer/src/workspace/hook/persistence/{useAutoSave,useBootstrap,rehydrate}.ts`
  • `src/renderer/src/workspace/hook/actions/{history,session,initialHistory}.ts`
  • `src/renderer/src/workspace/hook/ipc/useIpcSubscriptions.ts`
  • `src/renderer/src/workspace/tile-tree/TileLeaf/{useClaudeImagePaste,useComposerKeybinds}.ts`
  • `src/providers/{claude,codex}/runtime/{claudeSession,codexSession}.ts`

Where

The scatter itself is the issue — see inventory above.

Output

N/A — this is a structural / maintainability issue, no crash.

State

Fragmentation accumulated over months. Each individual addition was reasonable on its own; the aggregate is the problem.

Sketch

Three layered moves. The first is the load-bearing one; the others fall out of it.

Step 1 — One canonical home for all debug code: `src/debug/`.

A top-level `debug/` directory (peer to `src/main/`, `src/renderer/`, `src/shared/`) that subsumes everything in the inventory above. Internal shape:

```
src/debug/
shared/ # types, the Logger interface, log levels
Logger.ts # the typed logger boundary (see Step 2)
types.ts
main/ # main-process implementations
journals/ # ghost / dictation / paste / feed-debug
performance/ # PerformanceService, watchdog, OTEL exporter
storage/ # debugBundle, debugRetention
ipc/ # debug + pasteDebug IPC handlers
preload/ # preload bridges for debug IPC
renderer/ # renderer-side debug code
panels/ # DebugPanel, ProxyDebugPanel, FeedDebugPanel,
# HtmlDebugPanel, PerformancePanel,
# SystemPerfHeader+popover
devModules/ # the slot for #101's dev debug modules
runtime/ # feedDebug runtime helpers, renderTrace
```

`debug/` is logically a package even if it isn't published — same role headless packages play (cleanly separable, self-contained). The non-debug app code only depends on it through the `Logger` interface (Step 2) and a small number of IPC channels.

Step 2 — A typed Logger boundary.

Replace every `console.log` / `console.warn` in the 30 scatter files with calls to a typed logger:

```ts
// src/debug/shared/Logger.ts
export type LogLevel = 'debug' | 'info' | 'warn' | 'error'

export interface Logger {
debug(area: string, msg: string, data?: Record<string, unknown>): void
info(area: string, msg: string, data?: Record<string, unknown>): void
warn(area: string, msg: string, data?: Record<string, unknown>): void
error(area: string, msg: string, error?: unknown, data?: Record<string, unknown>): void
child(area: string): Logger // returns a logger with area pre-bound
}
```

The implementation routes to:

  • console (gated by log level + env var, same idea as `AGENT_CODE_PERF`)
  • the existing journal pipeline where appropriate (`PerformanceService`, ghost log, etc.)
  • the structured debug surfaces (the `area` is what lets the debug panels filter by subsystem)

Then each scatter site becomes:

```ts
// before
console.warn('[worktree-activity] background refresh failed:', err)
// after
log.warn('worktree-activity', 'background refresh failed', { error: err })
```

Cleaner site, structured data, routable to multiple sinks, filterable in the debug panel.

Step 3 — Enforce the boundary in lint.

Add an ESLint rule (`no-restricted-syntax` or `no-console`) that forbids raw `console.*` calls outside `src/debug/`. The 30 scatter sites get touched by Step 2's migration; the lint rule prevents the scatter from coming back.

Process:

This is too big for one PR. Sequence it as:

  1. PR α: create `src/debug/shared/Logger.ts` and a minimal implementation that routes to console. No migrations yet. Pure addition.
  2. PR β: relocate the 8+ existing debug directories under `src/debug/`. Pure move — no logic changes. The diff is mostly import-path updates. CI catches regressions.
  3. PR γ — δ: migrate the 30 scatter sites to the typed logger. Group by area (renderer-workspace hooks, providers, app shell) so each PR is reviewable. Roughly 4–6 small PRs.
  4. PR ε: enable the lint rule. Final step — only flips on after every scatter site has been migrated, otherwise the rule fails CI.
  5. PR ζ: cleanup audit. Walk the new `src/debug/` looking for redundant logger types, overlapping panels, journals that could be consolidated. (Out of scope for the first round; file follow-up issues if structural issues are found.)

The whole thing is several PRs over a couple of sessions. The payoff: one place to look for debug behaviour, structured logs, a discoverable mental model for new contributors, and a lint rule that prevents the scatter from regrowing.

Related: #101 (the dev-debug module slot — its `devModules/` directory becomes part of `src/debug/renderer/devModules/` once this consolidation lands).

Activity

  1. Juliusolsson05 commented on Sep 27, 2026

    @Juliusolsson05
    OwnerAuthor

    Closing (owner decision, 2026-09-27). Moving all debug code into one folder, adding a typed logger for every console call, and a lint gate would be heavy scaffolding with no user-facing gain. Raw console use grew from 30 to 94 files since this was filed, which shows the cost. Reopen / refile when: a concrete need appears, e.g. filtering logs by area in the debug panel. Then add a small logger for just that.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    needs-ownerOwner decision neededtype:choreMaintenance, deps, tests, docs

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions