How It Works

Domscribe operates in four stages: Inject, Capture, Relay, and Agent. Together they bridge the gap between a running web application and the AI coding agent that edits its source code.

Domscribe architecture — Inject, Capture, Relay, Agent


1. Inject

The bundler plugin (Vite, Webpack, or Turbopack) intercepts each source file during development and runs it through Domscribe's AST transform pipeline.

What happens at build time:

  1. The parser (Acorn for JS/JSX, Babel for TS/TSX, or VueSFC for Vue single-file components) produces an AST.
  2. The injector walks the AST and finds all JSX opening elements (or Vue template elements).
  3. For each element that does not already have a data-ds attribute, the IDStabilizer generates a deterministic ID using xxhash64(fileContent) combined with a position cache. This ensures IDs are stable across HMR cycles (greater than 80% cache hit rate).
  4. The injector inserts data-ds="<id>" into the element via magic-string, preserving source maps.
  5. Each mapping (element ID to file, line, column, tag name, component name) is recorded as a ManifestEntry.
  6. The BatchWriter buffers entries (50 entries or 100ms flush interval) and appends them to .domscribe/manifest.jsonl.

The manifest is append-only with hash-based staleness tracking. Each entry carries the xxhash64 of its source file at write time. When a file changes, new entries get a new hash, and readers automatically discard stale entries. The ManifestCompactor periodically removes entries for deleted files.

2. Capture

Framework adapters extract live runtime context from the running application in the browser.

React adapter: Walks the React Fiber tree to extract component props (from fiber.memoizedProps), hook state (traversing the memoizedState linked list), and component metadata. Three capture strategies are available: devtools (uses React DevTools hook), fiber (direct Fiber access), and best-effort (tries multiple strategies).

Vue adapter: Resolves DOM elements to Vue component instances via __vueParentComponent, unwraps reactive proxies (using toRaw/toValue), and supports both Composition API and Options API components.

The overlay UI -- built with Lit web components inside shadow DOM to prevent CSS/JS conflicts with the host application -- lets developers click any element and see its full context: props, state, source location, and component tree.

3. Relay

A localhost Fastify daemon connects the browser and coding agent. It provides three interfaces:

A file lock at .domscribe/relay.lock prevents duplicate daemon instances across dev server restarts. The relay auto-starts when the dev server boots (configurable via relay.autoStart).

4. Agent

The coding agent connects via MCP and uses Domscribe's 12 tools and 4 prompts to interact with the running application.

Code to UI: The agent calls domscribe.query.bySource with a file path and line number. The relay looks up the manifest, sends a CONTEXT_REQUEST to the browser via WebSocket, and returns the live DOM snapshot, props, and state.

UI to Code: A developer clicks an element in the overlay, describes a change, and submits it as an annotation. The agent calls domscribe.annotation.process to atomically claim the next queued annotation, navigates to the source file, implements the change, and responds via domscribe.annotation.respond. The overlay shows the response in real time via WebSocket.


Design Principles

Schema-First Architecture

All data structures are defined as Zod schemas in @domscribe/core. These schemas serve as the single source of truth for validation, TypeScript type inference, and runtime checking across all packages.

Module Boundaries

Dependencies between packages are strictly enforced via @nx/enforce-module-boundaries at lint time:

Scope TagAllowed Dependencies
scope:corescope:core
scope:infrascope:core, scope:infra
scope:buildscope:core, scope:infra
scope:adapterscope:core, scope:infra, scope:build, scope:adapter

Zero Production Impact

All Domscribe instrumentation is stripped in production builds. The transform plugin only runs in development mode, the overlay is aliased to a no-op stub in production, and the relay daemon is never started outside of development. This is enforced in CI.

Append-Only JSONL Manifest

The manifest uses an append-only JSONL format for crash safety. Instead of rewriting the file on every change, new entries are appended. Hash-based staleness tracking (two-pass read algorithm) ensures only the latest entries per file are considered valid. Compaction runs periodically to garbage-collect stale entries.

RFC 7807 Error System

All errors across the system follow the RFC 7807 Problem Details format via DomscribeError, providing structured error codes, titles, details, and HTTP status codes for consistent error handling.