Skip to content

Add an extensible Dev Debug Panel — schema-driven slot for hyperspecific, default-off debug modules #101

Closed

Description

@Juliusolsson05

Use case

We routinely hit bugs where the right diagnostic is hyperspecific: log THIS regex match against THIS screen state every 100 ms, dump THIS event queue's depth on each tick, watch THIS reducer's state transitions. Today there is nowhere clean to put that kind of instrumentation. Options are:

  • Drop it inline as `console.log` and remember to remove it (gets forgotten / committed accidentally).
  • Cram it into one of the existing debug panels (`DebugPanel`, `ProxyDebugPanel`, `FeedDebugPanel`, `HtmlDebugPanel` under `src/renderer/src/features/debug/ui/`), which were never designed to host one-off investigations and would balloon.
  • Add a per-bug debug feature with its own setting, IPC channel, lifecycle — overkill for an investigation that lives a few days.

What we want is a Dev Debug Panel that's a slot, not a panel — a host for opt-in, default-off, schema-declared debug modules. Adding a new module is cheap (declare it, register it, ship); removing it after the bug is fixed is cheap (delete its file). The modules are isolated from each other so two open investigations don't entangle.

Example

Concrete current need: the Claude paste / submit detection issue (#90) is still not deterministic after PR #71 / #72 / #80. To diagnose, we'd want a module that, while enabled, logs:

  • every 100 ms tick: the current screen-state snapshot the screen-parser sees
  • the regex match results of every submit-detection rule against that snapshot
  • the timestamp delta between the last user input event and the last detected submit transition

Today there's nowhere to put that. With the Dev Debug Panel: declare a module `ClaudePasteDetection` with a render component, an enable toggle, and whatever per-tick instrumentation it needs. Flip it on while investigating, off when not. The module's code lives next to whatever it's debugging.

(Optional) Sketch

Surface shape:

  • A new panel `src/renderer/src/features/debug/ui/DevDebugPanel.tsx` that lists registered modules and renders the active one(s). Always rendered but hidden unless a module is enabled.
  • A registry: `src/renderer/src/features/debug/devModules/registry.ts` — an array of module descriptors that the panel reads at render time.
  • A per-module file under `src/renderer/src/features/debug/devModules//` containing the module's code in one place.

Module descriptor (the easy-to-add schema):

```ts
type DevDebugModule = {
// Stable id for the toggle. Persisted to localStorage so the
// setting survives reloads — turning a module on while debugging
// a slow leak that takes hours to manifest must not reset on
// refresh.
id: string
// Visible title in the panel and toggle list.
title: string
// One-line description shown next to the toggle so the next dev
// knows what they're enabling.
description: string
// Author and the date the module was added. Old modules that the
// panel has carried for >N months without being touched are
// candidates for deletion at cleanup-PR time.
added: { author: string; date: string }
// The component rendered inside the panel when the module is on.
Component: React.ComponentType
// Optional: hooks the module needs to install at module-enable
// time (subscriptions, intervals, log handlers). Called when the
// toggle flips on, cleanup on off.
onEnable?: () => () => void
}
```

Wiring:

  • One entry in the command palette: "Toggle Dev Debug Module → " that lets the developer enable/disable any registered module.
  • The panel itself is only visible when the env flag is on (e.g. `AGENT_CODE_DEV_DEBUG=1`) — same gating philosophy as the system-perf header. Ship-safe by default; zero render cost when off.
  • Each module's enable state lives in localStorage so it persists across reloads. Modules clean up after themselves on disable via the `onEnable` returned teardown function.
  • The panel is mounted somewhere unobtrusive — bottom of the workspace, collapsible, doesn't fight with normal UI.

For #90 specifically:
A `ClaudePasteDetection` module would live at `src/renderer/src/features/debug/devModules/ClaudePasteDetection/` and contain:

  • `module.tsx` (the descriptor + Component)
  • `logger.ts` (the 100 ms interval that records screen state + regex matches)
  • `schema.ts` (whatever local types it needs)

When the bug is fixed and the module is no longer useful, the entire directory deletes cleanly. No registry surgery beyond removing the import.

What this is NOT:

  • Not a replacement for the existing structured debug panels (`DebugPanel`, `ProxyDebugPanel`, etc.) — those are diagnostic surfaces for known subsystems with stable shapes. The dev debug panel is for one-off investigations.
  • Not user-facing telemetry. Modules can be noisy, intrusive, and only safe behind the env flag.
  • Not committed-once-and-forgotten. Modules that outlive their investigation either get promoted to a structured panel or deleted.

Cleanup discipline:
Add a "dev debug audit" note to the cleanup-PR rhythm — every N months, walk `devModules/` and delete modules whose bug is closed and whose `added.date` is >N months old.

Related: #90 (the concrete present-day need); the bug class is general enough that any future hyperspecific investigation would use the same mechanism.

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

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions