Skip to content

Chapter 58: Diagnostics and Devtools

Diagnostics is what makes the architecture inspectable.

The runtime records every event, command, module install, and error to a diagnostic stream. The stream is consumable — by a console-logging module, by an on-page debug overlay, by a production observability pipeline, by automated tests. Without the diagnostic surface, the architecture’s modularity would be opaque; with it, the architecture explains itself.

Chapter 44 built the educational version. This chapter develops the maintained @kitsune/devtools surface — the debug overlay, the trace visualizer, the integration with production observability platforms, the patterns for using diagnostics effectively.

The runtime emits a typed diagnostic stream. Every architectural operation produces an entry:

type DiagnosticEntry =
// Events
| { kind: 'event'; type: string; event: AppEvent; at: number }
| { kind: 'event-handler-start'; type: string; module: string; at: number }
| { kind: 'event-handler-end'; type: string; module: string; at: number; durationMs: number }
| { kind: 'event-handler-error'; type: string; module: string; error: SerializedError; at: number }
// Commands
| { kind: 'command-start'; type: string; command: AppCommand; at: number }
| { kind: 'command-end'; type: string; ok: boolean; durationMs: number; at: number }
| { kind: 'command-no-handler'; type: string; at: number }
| { kind: 'command-error'; type: string; error: SerializedError; at: number }
// Modules
| { kind: 'module-install'; name: string; at: number }
| { kind: 'module-install-error'; name: string; error: SerializedError; at: number }
| { kind: 'module-uninstall'; name: string; at: number }
| { kind: 'module-error'; name: string; phase: string; error: SerializedError; at: number }
// Shell lifecycle
| { kind: 'shell-event'; type: string; at: number }
// Boundaries
| { kind: 'boundary-attached'; surface: string; at: number }
| { kind: 'boundary-detached'; surface: string; at: number }

Each entry has a timestamp and structured fields. The full stream is what the debug overlay and observability integrations consume.

The Part IV version had a smaller entry set; the maintained version splits handler-start / handler-end / handler-error into distinct entries so consumers can measure handler timing. The split adds a small amount of data (a per-handler start/end pair instead of a single entry) but enables performance analysis.

The @kitsune/devtools package ships a debug overlay as a Lit custom element:

<kit-debug-overlay></kit-debug-overlay>

Drop it into any page where a Kitsune shell is running. The overlay subscribes to the runtime’s diagnostic stream and renders the live trace.

Features the maintained overlay provides beyond Chapter 44’s:

Live trace. Every event, command, and module operation appears in real time. The overlay caps the displayed entries (default last 500) to keep memory bounded.

Filtering. The user can filter by entry kind (events only, commands only, errors only), by event type (matches against the type string), by module name, by time range (last 5 seconds, last minute, all).

Search. A text search across the entries, looking at types, names, and serialized payloads.

Inspection. Clicking an entry expands it to show full details — the event’s context, the command’s payload, the error stack, the timing breakdown.

Performance view. A separate view shows handler durations across the recent trace. Slow handlers are highlighted; the user can drill into them.

Module view. A list of installed modules, each with the events and commands it subscribes to or handles. The view answers which modules respond to this event? and which module handles this command?

Boundary tree view. A visualization of the current shell’s boundary tree, showing each boundary’s context and which elements are inside.

Closed-loop visualizer. A diagram showing the architecture’s flow — interactions on the left, modules in the middle, state changes on the right — with the live trace highlighting which paths are active.

The overlay is a development tool. It’s not shipped to production by default. Production observability is handled separately.

For production, the architecture integrates with the team’s existing observability platform. The @kitsune/observability module:

import { observabilityModule } from '@kitsune/modules/observability'
const observability = observabilityModule({
provider: sentry, // or Datadog, Honeycomb, etc.
sampleRate: 0.05, // 5% of normal events
alwaysCapture: ['event-handler-error', 'command-error', 'module-error'],
redact: (entry) => ({ ...entry, event: { ...entry.event, payload: '[REDACTED]' } })
})
await shell.install(observability)

The module subscribes to the diagnostic stream, samples normal entries, captures all errors, and forwards to the provider. The redaction function lets the team strip sensitive payload data before it leaves the application.

The sampling rate controls cost. 5% sampling on a busy application produces enough data to spot patterns without overwhelming the observability infrastructure. The error capture is unsampled — every error reaches the provider — because errors are typically what the team needs to investigate.

The module is one of many production-grade defaults. The team can install it and have working production observability out of the box.

A few specific patterns the diagnostic surface enables.

Slow-handler detection. A sentinel module observes the stream, watches for event-handler-end entries with durationMs > threshold, and emits warnings.

const slowHandlerSentinel = defineKitModule({
name: 'slow-handler-sentinel',
onStart: ({ runtime }) => {
runtime.onDiagnostic((entry) => {
if (entry.kind === 'event-handler-end' && entry.durationMs > 100) {
runtime.emit({
type: 'runtime.slow_handler',
payload: { module: entry.module, type: entry.type, durationMs: entry.durationMs }
})
}
})
}
})

Other modules (observability, alerting) subscribe to runtime.slow_handler. The pattern produces real-time visibility into the architecture’s performance.

Error-rate alerting. A sentinel watches for event-handler-error and command-error entries, accumulates the rate, and alerts when the rate exceeds a threshold.

Audit completeness verification. A sentinel watches that every profile.saved event produces a corresponding audit.recorded event within a time window. If audit fails to record, the sentinel raises an alarm (this is the compliance use case from Chapter 69).

Diagnostic export. A test-running module can capture the diagnostic stream for the duration of a test, then export the stream as a structured artifact. The artifact becomes the test’s evidence — the trace of what the application did during the test.

Each pattern is a small module. The composition produces a self-aware application that surfaces its own operational state.

Chapters 59 through 63 walk through five complete applications — a settings panel, a profile editor, a design token editor, an admin table, and a mini prompt workbench. Each application exercises the architecture differently and demonstrates specific patterns.

After Chapter 63, Chapter 64 covers integration with React and other frameworks for migration scenarios. Chapter 65 closes Part VI with What Kitsune Is Not.

Take an application you’ve built. Install @kitsune/devtools and drop <kit-debug-overlay> onto the page. Use the application normally for ten minutes.

Then explore the overlay:

  1. Look at the live trace. Recognize the events and commands flowing through.
  2. Try the filters. Find all events whose type starts with profile. Find all errors.
  3. Try the search. Find every operation related to the user’s session.
  4. Open an event’s detail. Examine the context, payload, timing.
  5. Switch to the performance view. Find the slowest handlers.
  6. Switch to the module view. Trace which modules respond to which events.
  7. Switch to the boundary tree view. Inspect the current shell’s boundary structure.

Then introduce a bug:

  1. Make one of your modules’ handlers throw an error (or take too long).
  2. Trigger the relevant event.
  3. Use the overlay to find the failure quickly.

Reflect on:

  1. How does the overlay compare to React DevTools or Vue DevTools? (Similar in some ways — inspecting state, viewing the tree — different in that the overlay shows the architecture’s event flow rather than component rendering.)
  2. How would the overlay help during incident response? (Reproducing a user’s flow becomes possible because the events that fired are visible; the team can trace through the chain.)
  3. If you wanted to ship the overlay to production for power users (engineers debugging issues for customers, for example), what would change? (Likely: gated by a query parameter or flag; redacted to remove sensitive payload data; restricted to specific events.)

The overlay is the architecture’s accountability surface. Most production applications don’t have anything like it. The Kitsune team’s bet is that having the surface — and using it daily — makes the architecture’s modularity productive rather than mysterious.