Skip to content

Add self-correction loop: logs, call, and shot commands#30

Open
molefrog wants to merge 1 commit into
mainfrom
claude/agent-self-correction-aegbzt
Open

Add self-correction loop: logs, call, and shot commands#30
molefrog wants to merge 1 commit into
mainfrom
claude/agent-self-correction-aegbzt

Conversation

@molefrog

@molefrog molefrog commented Jul 7, 2026

Copy link
Copy Markdown
Owner

Implements the three-leg self-correction feedback loop for applets, allowing the agent to detect runtime failures, test server functions, and see what applets actually look like without waiting for user feedback.

Summary

Adds three new CLI commands (moi logs, moi call, moi shot) that close the gap between successful builds and actual runtime behavior. The agent can now:

  • Feel (moi logs) — query an in-memory error journal of applet runtime failures
  • Poke (moi call) — invoke server functions directly through the same worker pool as browser RPC
  • See (moi shot) — render applets to PNG through a live workspace tab

Key Changes

Server-side error journal (server/applet-log.ts)

  • Per-workspace, in-memory ring buffer (100 entries max) tracking applet failures
  • Records five error sources: build, load, render, window, rpc
  • Deduplicates identical errors (same source + attribution + message) by bumping a counter
  • Clears entries when an applet rebuilds successfully (old bundle no longer exists)
  • Entries include timestamp, source, applet attribution, message, optional stack, and occurrence count

CLI commands (server/cli.ts)

  • moi logs [--json] [--clear] — display or clear the error journal
  • moi call <module>/<fn> '[args]' — invoke a server function with JSON arguments, returns devalue-encoded result
  • moi shot widget|view <name> — screenshot an applet at its real grid size (or full panel for views)

Browser-side error reporting (client/lib/applet-log.ts)

  • Catches module load failures, render crashes, and window-level errors
  • Attributes errors to applets by matching stack frames against bundle URLs
  • POSTs errors to POST /api/workspaces/:id/applet-log with per-error cooldown (5s) to prevent storms
  • Always-on reporting: errors are recorded even if the user doesn't immediately report them

Applet screenshot support (client/lib/applet-shot.tsx, server/applet-shot.ts)

  • Mounts applets offscreen in the workspace's themed wrapper (so fonts/colors apply correctly)
  • Waits for mount-time data fetches to settle (configurable, default 1.5s)
  • Rasterizes with modern-screenshot and measures content overflow
  • Returns PNG + render facts (box dimensions, content size) to the CLI
  • Uses relay pattern (same as Scratchpad) to broadcast requests to live tabs

Control port integration (server/control.ts)

  • Refactored sendScratch → generic sendControl to handle multiple request types
  • Added handlers for logs, call, and shot request types
  • moi bundle output now nudges toward moi logs when errors are on record

Type definitions (lib/types.ts)

  • AppletKind'widget' | 'view'
  • AppletLogSource'build' | 'load' | 'render' | 'window' | 'rpc'
  • AppletLogEntry — full journal entry shape with dedup count
  • ShotRequest / ShotResult — screenshot request/response types

Generic relay utility (server/relay.ts)

  • Extracted relay mechanics (pending map, timeout, first-reply-wins) into reusable createRelay<Res>
  • Used by both Scratchpad (scratchpad-relay.ts) and applet shots (applet-shot.ts)

Error boundary updates (client/components/WidgetErrorBoundary.tsx)

  • Now reports render crashes to the applet log
  • Accepts kind (widget/view) and workspaceId for attribution
  • Optional onError callback for callers like applet shots

RPC error recording (server/api.ts)

  • Server-function failures now recorded in the journal with module/function attribution
  • Allows moi call to surface issues before the browser sees them

https://claude.ai/code/session_01M8rSCv39HrkfaH23tceb2q

The agent that builds applets could not tell when they broke: a widget
compiles, then fails to load in the browser, crashes on render, or its
server function throws — and nothing reaches the agent until the user
complains. Visual defects were worse: nothing but human eyes caught them.

Close the loop with three legs (spec: docs/self-correction.md):

- moi logs — a per-workspace, in-memory applet error journal. Server-side
  producers (RPC failures, build failures) record directly; the browser
  reports load failures, render crashes (error boundary), and window
  errors attributed by bundle-URL stack match. Identical errors dedup
  into a count; a successful rebuild clears the applet's standing
  entries; `moi bundle` ends with a nudge when errors remain on record.
- moi call — invoke a .server.ts function directly through the same
  worker pool/env/timeout as browser RPC. Args as one JSON array; the
  result renders through devalue (Map/Set/Date intact) with duration.
- moi shot — screenshot a widget (true grid footprint) or view (panel
  size) through a live workspace tab: offscreen mount inside the themed
  wrapper, modern-screenshot rasterization, plus box-vs-content render
  facts as an objective overflow signal. Fails fast when nothing is
  built or no browser is connected.

The scratchpad's pending-map relay generalizes into server/relay.ts with
the shot relay as a second instance. The moi-workspace skill grows a
"Verify your work" section teaching the loop (bump to 0.5.0). Also
replaces a stray literal NUL byte in views.ts with the '\0' escape so
the file diffs as text again.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01M8rSCv39HrkfaH23tceb2q
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants