Repository navigation
Add an interactive Agent Workbench with local host discovery and lifecycle replay #105
Description
Summary
Evolve the existing dev Workbench into the browser half of Agent Bundle's
meta-framework. It should derive its UI from the same compiled route, event,
target, and capability manifests as production; discover installed target hosts
and their real exposed tools/components on the local machine; execute or replay
those surfaces; and render the real Agent Document output as formatted
Markdown, structured data, rich content, progress, and boundary updates.
agent-bundle dev --open should provide an app-like development loop instead
of requiring authors to reason through raw MCP Inspector forms and JSON traces.
Existing foundation
The Workbench already has meaningful pieces to preserve:
- a build-scoped capability catalog derived from artifact inspection;
- Hooks pages that execute generated wrappers from canonical inputs and show
canonical/native mapping; - an MCP page with real client sessions and MCP App rendering;
- a Runtime Playground that creates and replays runs, reads Flight, and renders
a runtime stage; - last-good build behavior and dev rebuild events.
The missing feature is one compiler-derived application model that joins the
new filesystem routes, real Agent Document renderer, semantic event lifecycle,
and locally observed host/tool catalogs into one usable authoring experience.
Product model
agent-bundle dev --open
-> compile project route/capability manifests
-> discover selected installed local hosts and exposed components/tools
-> open Workbench
|- route catalog and generated input editors
|- host/target selector
|- event lifecycle timeline and replay
|- live Agent Document stage
|- formatted Markdown/rich-content view
|- target projection and protocol/native trace
`- rebuild diagnostics and last-good output
The Workbench reads authoritative framework and host adapter catalogs. It does
not keep a second handwritten list of routes, event payloads, or output
components.
Local host discovery
Each target adapter may implement a read-only local discovery probe that finds
the host installations/config roots it understands and returns actually
observable components:
interface ObservedLocalHost {
target: AgentTarget;
installation: Observed<HostInstallation>;
components: readonly ObservedHostComponent[];
tools: readonly ObservedTool[];
events: Readonly<Record<AgentEventKind, CapabilityState>>;
}The probe uses the same native paths, schemas, host CLIs, and MCP handshakes as
the target adapter. It may discover generated plugins, skills, commands, rules,
hooks/events, configured MCP servers/tools, Apps, or native extensions where
the host exposes them. If a proprietary built-in tool catalog is not observable,
the Workbench reports that boundary instead of inventing tools.
This is direct discovery, not a separate sanitized-profile architecture. The
feature does not add a general security or credentials subsystem.
Authoring journeys
Tool and route preview
- Select a compiled tool, prompt, resource, CLI command, script, or event route.
- Generate an input editor from the compiled schema and route config.
- Run the real development route with the same request context and providers
used by production. - Apply shell/progress/replace/error/complete events to the browser stage.
- Show formatted Markdown by default, with Agent Document and projection data
available for inspection.
Host-aware lifecycle replay
- Select an installed target host and one supported semantic lifecycle.
- Use a real observed native fixture/receipt or a checked-in adapter fixture as
the replay source, clearly identifying which one it is. - Decode native input through the target adapter.
- execute the generated semantic event route and renderer;
- encode the resulting native response or delivery route;
- show canonical input, request context, render timeline, final document,
native projection, and diagnostics in one correlated view.
Replay exercises the real generated wrapper and adapter codecs. A deterministic
replay is not labeled as proof that the external host dispatched the event.
Installed MCP/tool use
For an observed configured MCP server, reuse the Workbench's real MCP client
session to initialize, list, call, read, or retrieve prompts according to the
negotiated protocol. Do not replace a failed connection with a fixture and do
not write non-protocol output to the server's stdout.
Browser rendering
- The browser consumes the renderer's versioned
AgentDocumentSnapshotand
ordered event stream. Agent.Markdownrenders with an audited Markdown projector such as TanStack
Markdown.- Image, audio, resources, errors, and structured values have explicit visual
components and capability status. - Suspense fallbacks are replaced rather than left beside final content.
- Progress updates are shown as mutable invocation status.
- A target-projection switch shows what MCP, CLI, or a native event response
actually receives without changing the canonical document.
Development lifecycle
agent-bundle dev --openopens the Workbench after the first valid build.- Adding/removing/renaming a route updates navigation from the new compiler
manifest without a browser reload. - Source errors retain and label the last-good route catalog and rendered run.
- Rebuilds cancel stale runs and prevent old events from updating a new build.
- Saved replays remain bound to their original build, target contract, and
artifact identity.
Non-goals
- A replacement for native host acceptance tests.
- Guessing proprietary built-in host tools that expose no catalog.
- A second route/capability registry owned by the browser.
- Implementing collision detection or TraceDecay domain behavior in Workbench.
- Presenting adapter fixtures as live host activity.
Acceptance criteria
- A framework-mode project opens a populated browser Workbench from one dev
command with no manually authored Workbench catalog. - The Workbench renders real Agent Document Markdown, structured output, rich
content, progress, Suspense replacement, and final status from one invocation. - A compiled MCP tool can be invoked through a real MCP client session and its
final output appears through the same Agent Document stage. - At least two materially different installed target hosts are discovered
through their real adapter probes, with observable tools/components listed. - A semantic lifecycle replay crosses native decode, generated route execution,
RSC rendering, and native encode, and identifies fixture versus observed
receipt provenance. - Unobservable host tools/events appear unavailable rather than fabricated.
- Route changes, invalid source, last-good output, cancellation, and stale replay
identity have browser and framework Rstest coverage.
Design references
- Current capability-aware Workbench design
- Current build-scoped Workbench capability catalog
- Current generated Hook simulation and replay UI
- Current Runtime Playground and Flight run support
- Current MCP client/App Workbench surface
- TanStack Markdown architecture
- Rsbuild development server API
Stack position
- Depends on: #93 routes, #95 context/providers, #96 RSC renderer, #97 events, and #103 Rstest.
- Uses #100 host components as its expanded local discovery catalog when that lands; existing target adapters provide the first probes.
- Complements #104 worktree-proximity by making its routes, replay, and
rendered warnings inspectable in the browser.
Full meta-framework stack
- Foundations: #93 routes - #94 identity - #95 context - #96 renderer
- Runtime: #97 events - #98 state - #99 notices
- Host: #100 components - #101 lifecycle
- Surfaces and proof: #102 CLI/scripts - #103 Rstest - #104 proximity example - #105 Workbench
Activity
feat: compile route-graph IR and inspect focus (#93) (#119)
feat(workbench): compiler-manifest-driven navigation and route catalog (
feat(workbench): schema-driven route input editors over a static mani…
feat(workbench): Agent Document stage over server-decoded render even…
ScriptedAlchemy commentedon Sep 1, 2026
Stage 2 landed (per #107 rev 2's staging: "Agent Document stage + Markdown projection", plus the stage-1 handoff's schema-driven input editors). Four merges:
d67f84c2f) — stage-1 post-merge review fixes: CLI usage arity matching generated help, externally packaged (custom/command/remote) server surfaces retained in the catalog, and the missing stale→repair browser acceptance leg.101e70bf7) — schema-driven input editors. The bounded zod-AST machinery behind the Generate CLI, MCPorter, and script projections from the compiled route graph #102 argv grammar now also projects any conventional route'sexport const inputSchemainto a deterministic JSON-Schema subset — statically, modules never execute. Wire pair per the stage-1 handoff: new optionalinputSchemaonRouteManifestRoute+ the strictrouteSchemadecoder extended in the same PR; no other DTO widened. Routes-page editors render generated fields (scalars, enums, booleans, repeatable arrays, defaults, descriptions, required markers) with validation before invoke; out-of-grammar schemas get an honest raw-JSON fallback with no diagnostics. Tool routes hand validated input to the existing MCP page as a prefill (real sessions + consent unchanged, nothing auto-executes); CLI routes emit a copyable argv invocation.690132404) — review-driven correctness: repeated-flag usage markers, optional-boolean unset state (untouched optionals are omitted, notfalse), and stale prefills can no longer attach arguments to a different tool.8a5b42586) — the Agent Document stage. Flight stays internal: the dev server decodes a succeeded run's stored Flight through the optional@agent-bundle/runtimepeer (GET /api/runtime/runs/:id/document, same-session guard, AB8207/AB8208 honest diagnostics) and the Workbench strictly decodes theshell/progress/replace/error/completesequence with its own zod schemas. A shared stage renders Markdown through the audited projector the Skills page already used, plus text/context/json/progress/image/audio/resource/error nodes, accumulated render diagnostics, live progress, final status, and an inspectable event timeline — surfaced as a Document view in the Runtime Playground. Real-Chrome proof: a hook run's stored Flight renders as a real document; an MCP-element run shows the honest decode diagnostic instead of a fabricated document.Wire-shape note for later stages:
documentToCallToolResultdeliberately lowers documents into plain MCP content blocks +structuredContentfromdocument.value— the AgentDocument is not on the MCP protocol wire. Compiled MCP tools therefore reach the stage today via their runtime runs; putting live MCP protocol calls through the stage needs the run-correlation seam that stage 3's lifecycle replay owns.What stages 3–4 still need (unchanged per #107):
ScriptedAlchemy commentedon Sep 1, 2026
Stage-2 follow-ups, for the record: #234 (
a0c2add8b) folds the two post-merge Codex findings on #231 — the document endpoint now enforces a named 16 MiB aggregate response budget with an AB8209 (413) diagnostic instead of unbounded snapshot accumulation, and document markdown resolves onlydata:images so model-produced output cannot trigger remote fetches. #235 (64aaab0b2) repairs the two Verify failures #231's pending-only merge let onto main: the optional@agent-bundle/runtimepeer had leaked into the emitted root declarations (packed consumers couldn't compile) and a document-stage locator needed exact matching. Verify is green again on the fix branch across Node 22/24/26.feat(workbench): migrate the Agent Document panel to @effect/atom-rea…
ScriptedAlchemy commentedon Sep 2, 2026
effect-atom phase-1 pilot landed — PR #250, merge
4d84f848b67b47fbc45845f4abdefb97b132b6fb.What landed
RegistryProviderin the Workbench app shell (main.tsx); the module-level default registry is never used.RuntimeDocumentPanelmigrated from a hand-rolled request union +AbortController+ stale-run guards to anAtom.familykeyed by run id in a dedicated browser-state module (src/runtime/agent-document-atoms.ts), consuming the existing strictly-decodedagent-document-client.tsoutput. Zod decoders andProjectClient/RuntimeClientlifecycles untouched;Effect.tryPromisemaps fiber interruption to the requestAbortSignal.effect@4.0.0-rc.112,@effect/atom-react@4.0.0-rc.112,scheduler@0.27.0(official v4 packages; no legacy@effect-atom/*).docs/effect-conventions.md, including theeffect/unstable/reactivityadoption-table row and re-pin lockstep steps (bump atom-react in the same chore, re-run disposal test, re-measure bundle).runtime-document-atoms-disposal.test.ts, integration pool): 5 never-resolving mount/unmount cycles → exactly one abort per unmount; 5 resolving cycles → clean re-render, bounded loader calls; zero page errors.agent-bundle(the Workbench dist ships inside the published package).Bundle delta (workbench production build)
One-time cost of the
effectcore + atom runtime entering the shared chunk; subsequent phases reuse it at ~zero marginal bundle cost.Verification
Typecheck, lint, full unit pool (2330 passed), inspector + disposal integration tests, and the workbench browser e2e at 1440×900 (
runtime-playground.e2e.test.tspopulated Document + decode-diagnostic flows;overview.e2e.test.tspopulated + stale-diagnostic/repair flows) — 16/16 green locally, re-run after rebasing over #238; CI 9/9 green on the PR.Phase 2+ follow-ups (not in phase 1)
CapabilityStatemachine inmain.tsxis the natural next candidate; stale-while-revalidate maps ontoAsyncResultwaiting states).@effect/atom-reactin lockstep, lift the stream-atom ban if the disposal fix is included, re-run the disposal regression, re-measure the bundle (steps now codified indocs/effect-conventions.md).feat(workbench): route editor state on effect-atom (#105 phase 2)
Merge pull request #263 from ScriptedAlchemy/feat/workbench-atom-phase2
ScriptedAlchemy commentedon Sep 2, 2026
effect-atom phase 2 landed — PR #263, merge
c97a50f4125528844d59cfc9dd3660ef0d14c6f1.What landed
useState/derivation clusters per editor: draft, raw JSON, errors, rawError, attempted, arguments, argv) migrated to a writableAtom.familykeyed by manifest digest + compiled route id, in a new dedicated browser-state modulesrc/routes/route-editor-atoms.ts(phase-1 pattern; rootRegistryProviderreused).routes-model.tsstays pure: newRouteEditorState+ pure transitions derive from the untouchedvalidateRouteInput/validateRawRouteInput/cliCommandUsage/cliCommandInvocationprojections. No stream atoms (ban holds until the post-rc.112 re-pin), noAtom/AsyncResultin exports, components use@effect/atom-reacthooks only.route-editor-atoms-disposal.test.ts, integration pool): realRoutesPagein a browser fixture, 5 mount → type → validate → unmount → remount cycles each showing fresh initial state (editor atoms release on unmount), plus an A→B→A digest-switch cycle; zero page errors.agent-bundle.Bundle delta (workbench production build)
Near-zero as expected — the effect core + atom runtime cost was paid in phase 1.
Verification (local, after rebasing over #259)
Lint 0/0, scoped routes units 40/40 (contract files unmodified), full integration + workbench browser e2e 61 files / 629 tests: 625 passed, 3 skipped, 1 failed — the failure is
public-api.test.ts :: keeps bundled config extension types in emitted root declarations, reproduced identically on a pristineorigin/mainworktree (pre-existing, unrelated to the workbench). Also pre-existing on main:pnpm typecheckfails with TS7016 inclassify-docs-only.test.ts(from #259, flagged there); this PR's diff typechecks clean.Remaining phases (unchanged from the phase-1 list)
CapabilityStatemachine inmain.tsx; stale-while-revalidate maps ontoAsyncResultwaiting states.@effect/atom-reactin lockstep with the next effect RC, lift the stream-atom ban if the disposal fix ships, re-run both disposal regressions, re-measure the bundle.ScriptedAlchemy commentedon Sep 2, 2026
Decision (2026-09-01): the Effect re-pin chore (unblocks atom phase 4 stream pages) runs automatically when the next RC ships — lockstep atom-react bump, disposal regression, bundle re-measure, unstable-API re-check.
Merge pull request #279 from ScriptedAlchemy/wave/105-s3
ScriptedAlchemy commentedon Sep 2, 2026
Stage 3 landed — semantic lifecycle replay (PR #279, merge 9a43616)
A semantic lifecycle replay now crosses all four boundaries with the real production codecs, correlated with the #231 Agent Document stage, and identifies fixture versus observed receipt provenance:
validateNativeEventEnvelope(the per-target envelope validation formerly inlined as generated wrapper strings, now single-sourced in the events runtime and imported by the generated wrappers; artifact suites prove the wrapper behavior unchanged) →createCanonicalEventProps.react-serverchild (fork/IPC, flight-worker precedent), draining the full ordered feat(runtime): add Agent Document contracts #140 render-event stream (shell→…→complete, monotonic sequence).projectEventDocument, with per-target contract TypeErrors surfaced honestly aslifecycle.projection.unsupportedinstead of failing the replay.source: fixture | observedechoes through the wire and the UI banner states explicitly that a deterministic replay is not evidence the host dispatched the event; editing a starter fixture downgrades the run to observed.Surfaces:
GET /api/lifecycles(event routes × concrete hosts, native event names, contract revisions, checked-in starter envelopes validated by the shared validator, honest exclusion diagnostics) andPOST /api/lifecycles/replays(fail-closed manifest binding — AB8213/409 drives the Workbench stale→refresh→explicit re-run repair; AB8211/400 malformed envelopes with real validator messages; AB8214/413 named 16 MiB budget). Workbench#lifecyclespage is read-only, desktop 1440×900, per the binding browser-state conventions (no stream-backed derived atoms).Acceptance evidence (local, on the merged base): typecheck + lint clean; unit 39 ✓, route-unit replay ✓, integration 61 ✓ including a real-dev-server replay proof in the default (non-react-server) pool, plus 64 ✓ hooks/dev-workbench regressions; browser-mounted page tests 3 ✓;
lifecycles.e2e✓ covering populated claude + codex fixture legs, an observed receipt leg, and the stale-manifest→repair flow with zero page errors;overview+runtime-playgrounde2e regressions 16 ✓. Changeset:agent-bundlepatch.Out of scope, per the stage plan: stage 4 (host discovery UI) and stage 5 remain.
Merge pull request #285 from ScriptedAlchemy/wave/105-s4
ScriptedAlchemy commentedon Sep 2, 2026
Stage 4 landed — read-only host discovery (PR #285, merge
089caf248)The Workbench now surfaces the read-only install doctor (#236/#243) as a Hosts page, per the G6 gate in #107 (read-only discovery approved; #101 mutation never enters the Workbench):
runDoctordirectly as a library (src/install/doctor.tsis in the same package; no CLI shelling).GET /api/discovery(session-authorized, GET-only, named 16 MiB budget,AB8215–AB8218) serves a newsrc/contracts/discovery.tswire contract pinned to the doctor types by compile-time assignability; the service is single-flight with no result caching past settlement, and production wiring binds drift checks to the prepared build (from: <root>/dist+ its source revision).AB8234), pure honest-state model, dedicateddiscovery-atoms.tsbrowser-state module (third sanctioned module indocs/effect-conventions.md; no stream atoms), desktop 1440×900. Sections: per-host probe/version/evidence, installed inventory, bundle check with durable-state summary, runtime endpoints, and the full diagnostics list with recovery text.role=\"alert\"exists); unknown inventory says the host owns its registry; missing build → info, not error; endpoint-scan skips are honest skips.routes.sourceRevision(a review pass caught and fixed an initial route-graph-digest/source-revision identity mismatch) and shows a stale banner after a real rebuild, repaired by explicit re-run.Acceptance evidence (local, on the merged base): lint 0/0; typecheck across all three projects; full unit + route-unit pools green; integration + real-Chrome e2e at 1440×900 —
host-discovery-dev-server(real dev server, scripted doctor runner, auth + probe assertions),discovery.e2e(populated Claude/Cursor + honestly-absent Codex + stale-socket endpoint + stale→repair via a real watched-source rebuild, zero page errors),discovery-atoms-disposal, plus regressionslifecycles.e2e,overview.e2e(13),runtime-playground.e2e(3), and both prior atom-disposal suites — no failures. Changeset:agent-bundlepatch.Remaining: stage 5 — user-initiated live MCP probing with consent + redaction (the live-probe/controller seam). Discovery here is static observation only; live MCP handshakes stay out until that stage.
Merge pull request #299 from ScriptedAlchemy/wave/105-s5
ScriptedAlchemy commentedon Sep 2, 2026
Stage 5 landed — user-initiated live MCP probing with consent + redaction (PR #299, merge
e613e0037). This was the final stage.The Hosts page can now probe a discovered MCP endpoint live, under the #107 G6 gate exactly as decided: probes are structurally read-only, anything that executes is user-initiated, and no mutation surface enters the Workbench.
DiscoveryBundleFinding.mcpServers, read via each adapter's MCP runtime manifest — absent = honestly not enumerated, empty = declared none). The probe request carries only{ host, serverName }; the dev server re-resolves the bundle root from disk through the same doctor machinery and reuses the existing MCP session launch/transport machinery (resolveMcpSessionLaunch,@modelcontextprotocol/clientstdio + streamable-http). Browser input never becomes a launcher, path, env, or JSON-RPC frame — the same discipline the MCP session routes pin.POST /api/discovery/probes(session-authorized,AB8219–AB8223) performs exactlyinitialize→tools/list→ capability snapshot → close. The wire admits no operation selection;tools/callis unreachable from this surface. Strict 10 s total budget with transport teardown, named 16 MiB response budget, bounded tool/instruction text, per-target single-flight with nothing cached past settlement, plugin-data tempdir always removed.[REDACTED]args, env allowlist — real env values never leave the server, sanitized URLs); every server-reported string passes credential redaction plus the dev-log absolute-path precedent (bundle paths become<bundle>/…, any other absolute path fails closed to[REDACTED]).Unreachable/Timed outbadge with the redacted failure detail — no error wall,role=\"alert\"only for real request/decode failures. The strict zod client (AB8235) rejects reports violating the ok⇔snapshot / down⇔failure invariants.discovery-atoms.tsbrowser-state module keyed to the current report view; re-running discovery drops them; in-flight probes abort on unmount (disposal-tested). Nothing persists.Acceptance evidence (local, on the merged base): lint 0/0; typecheck across all three projects; unit 2560 ✓, route-unit 24 ✓, projection 57 ✓; real-dev-server integration proof (authenticated probe launches the example's real prebuilt stdio server: initialize + tools/list ✓, env-allowlist assertion ✓, 403 without session ✓); real-Chrome e2e at 1440×900 —
discovery.e2eextended with the consent flow (zero probe requests before confirm, Cancel sends nothing, exactly one POST), a populated live probe of the realtimelineserver, an honest neutral down leg, ephemeral-result cleanup on re-run, and the stale→repair regression, zero page errors; regressionsoverview.e2e13 ✓,runtime-playground.e2e3 ✓,lifecycles.e2e✓,host-discovery-dev-server✓,lifecycle-replay-dev-server✓, all three atom-disposal suites ✓. Changeset:agent-bundlepatch.Closure evaluation follows in the next comment.
ScriptedAlchemy commentedon Sep 2, 2026
Closing #105 — all five stages landed; every acceptance criterion has merged, tested evidence.
Stages: s1 manifest navigation #211 · s2 schema-driven editors + Agent Document stage #222/#224/#226/#231 (+#234/#235 riders) · s3 semantic lifecycle replay #279 (
9a43616a5) · s4 read-only host discovery #285 (089caf248) · s5 user-initiated live MCP probing with consent + redaction #299 (e613e0037). Browser-state migration riders: #250/#263.Acceptance criteria, verified against the landed evidence:
agent-bundle dev --opendrives navigation and the route catalog from the compiled route manifest (feat(workbench): compiler-manifest-driven navigation and route catalog (#105 stage 1) #211); no browser-owned registry exists (the drift check pins the catalog to the compiler pass).data:-only images hardened in fix(workbench): bound Agent Document responses and keep document markdown images controlled #234.Unreachable/Timed outreport, not an error wall (feat(workbench): user-initiated live MCP probing with consent + redaction (#105 stage 5) #299).overview.e2e; stale-run/abort discipline pinned by the three atom-disposal suites; fail-closed stale identity proven for replays (AB8213 → stale→refresh→re-run, feat(dev): semantic lifecycle replay (#105 stage 3) #279), discovery reports (digest comparison → stale banner → explicit re-run, feat(workbench): read-only host discovery page over the install doctor (#105 stage 4) #285), and probe results (ephemeral, dropped on re-run, feat(workbench): user-initiated live MCP probing with consent + redaction (#105 stage 5) #299).Boundaries that remain by decision, not omission: G6 (#107) keeps every mutation surface out of the Workbench permanently — live interaction with installed endpoints is bounded to the consented read-only initialize/tools-list probe (calling installed servers' tools stays out by gate; the project's own compiled servers retain full consented sessions on the MCP page); the cursor adapter has no MCP runtime contract today, so cursor bundles honestly enumerate nothing; any probing polish beyond installed-bundle endpoints (per #107 §post-arc) and the #108 source-vs-generated Skill comparison view are their own evidence-backed increments outside this issue.
fix(docs,create): close the audited #94/#97/#104/#105 remainders (#372)
ScriptedAlchemy commentedon Sep 3, 2026
Closed-issue re-audit (G3) — VERIFIED, one docs gap fixed in #372 (squash
ccb9cd18a).Re-run on
origin/main(af1c18551), all green:lifecycle-replay-dev-server,host-discovery-dev-server(2),mcp-probe-dev-server, the three atom-disposal suites,lifecycles-page.browser(3),lifecycles.e2e,discovery.e2e,runtime-playground.e2e(3),overview.e2e(13) +examples-real.e2e(5). Code: manifest-driven routes page +route-editor-atoms.ts, Agent Document stage +agent-document-atoms.ts(GET /api/runtime/runs/:id/document,dev/runtime-routes.ts:420-452), lifecycle replay (dev/playground/lifecycle-replay-routes.ts:52-53), discovery overrunDoctor(dev/playground/host-discovery-service.ts:172), consented probes (dev/playground/mcp-probe-routes.ts), three sanctioned browser-state modules listed indocs/effect-conventions.md:187-188.Gap:
AB8200–AB8214(runtime routes, document endpoint, lifecycle replay) andAB8233–AB8235(browser strict decoders) were cited as "honest diagnostics" in the stage comments butdocs/diagnostics.mdcovered them only by the genericAB8xxx"Development server configuration" row. #372 adds explicit rows with the meanings taken from the emitting sites.Noted, by design (closing comment item 3 + the stage-2 wire-shape note): a compiled generated-server tool run from the MCP page's real client session is not rendered through the Document stage — the AgentDocument is never on the MCP wire — so the stage shows documents for runtime runs, and the only e2e document proof is a hook run (
runtime-playground.e2e.test.ts:150-165; the MCP-element run asserts the honest decode diagnostic).