Appearance
14 — Implementation Plan
NOTE
Status: Draft. Turns this spec into ordered, verifiable milestones. Each milestone lists scope, expected files, included APIs, required tests, acceptance criteria, dependencies, and top risks. A milestone is done only when its acceptance tests pass and the docs it implements needed no undocumented decisions — if an implementer had to invent behavior, that's a spec bug to fix in the same PR.
Sequencing rationale
The originally proposed progression (domain model → registry → lifecycle → snapshot → invocation → policies → React → testing → oRPC → example → WebMCP) is sound but improved in three ways:
- The core test harness moves before React (M2, not after it). The harness is the reference consumer; building registry features test-first against
createTestSurfacekeeps seams honest and makes every subsequent milestone self-verifying. The React half of the testing package still lands after React (M7). - Confirmation is folded into the policy milestone (M6) rather than floating: it is a policy outcome plus a store, and testing it needs the pipeline.
- The example app is built alongside the oRPC adapter (M9–M10 overlap), because 10-examples.md is the acceptance artifact for both.
Milestones are strictly ordered by dependency; within one milestone, tasks parallelize freely.
M0 — Repo scaffold
- Scope: pnpm workspace; packages
core,react,orpc,testing,webmcp(stub),examples/devices-app(stub); tsup builds (ESM + d.ts), vitest, TypeScript strict, publint + size-limit + Changesets; CI matrix (React 18/19, TS current/next). - Files:
pnpm-workspace.yaml, per-packagepackage.json/tsconfig, root vitest config,.github/workflows/ci.yml. - Tests: CI runs an empty suite green; size-limit wired.
- Acceptance:
pnpm build && pnpm testgreen on a clean clone. - Risks: none meaningful.
M1 — Identity, schema layer, definition model
- Scope: id grammar + parse/format/validate + wire codec;
AgentSchema,fromStandardSchema,fromJsonSchema+ subset validator (D19);JsonValueguards; definition types;action/observation/defineAgentComponenthelpers; definition validation (caps, plane rule, duplicate capability names);AgentSurfaceDefinitionError. - Files:
core/src/{ids,schema,definition,errors}.ts. - APIs: 03 §schemas, §definitions; 07 §registration-time.
- Tests: grammar accept/reject table; wire codec round-trip + overflow hashing; subset validator vs a fixture corpus (accepted + rejected schemas); Zod 4 + Valibot through Standard Schema; every
AgentSurfaceDefinitionErrorcode reachable. - Acceptance:
prototypes/api-typecheck.tscompiles against the real types unchanged (inference parity with the spec's examples). - Depends: M0. Risks: subset validator correctness — mitigate with fixture corpus reused by testing pkg.
M2 — Registry core + test harness (framework-free)
- Scope:
createAgentSurfaceRegistry; registration/unregistration; handles (update,invalidate, dead handles); collision handling (D4); availability computation;surfaceId/surfaceVersion(D1/D2); event dispatcher with D17 guarantees; injectable clock;createTestSurfacewithevents(),snapshot()(minimal),as(). - Files:
core/src/{registry,events}.ts;testing/src/harness.ts. - Tests: lifecycle register→unregister; Strict-Mode-like double register/unregister symmetry; collision reject/replace; version bump matrix (what bumps, what doesn't); event ordering incl. re-entrancy and throwing listeners.
- Acceptance: every guarantee sentence in 02 §guarantees has a named test.
- Depends: M1. Risks: re-entrancy semantics — encode the spec examples as tests first.
M3 — Snapshot projection
- Scope: descriptor projection (frozen JSON,
internalstripped), deterministic ordering,scope/includeUnavailablefilters, budget +truncated(Experimental flag), route info, per-registration descriptor caching. - Files:
core/src/snapshot.ts. - Tests: snapshot purity (no
read()calls — spy);internalnever serialized (deep scan); ordering stability across mount orders; budget drop order by priority. - Acceptance: semantic-snapshot fixture of a synthetic surface matches 03 §snapshot shapes field-for-field.
- Depends: M2. Risks: low.
M4 — Invocation pipeline + error model
- Scope: the 10-phase pipeline (02, as corrected by 18) minus policy phases (stub pass-through); resolution incl. tombstones,
AMBIGUOUS_INSTANCE, staleness rules (D1); effective-input construction; preconditions; concurrency (D13/D24: action queue + bounded observation admission); consumer-scoped conflict-safe dedupe (D22); timeout/cancel (D15); unmount-mid-flight (D16) + navigation settlement (D23); result envelopes; fullAgentCapabilityErrorPayloadserialization + sanitization. - Files:
core/src/{invoke,errors}.ts(+ harnessinvoke/observe/captureRef). - Tests: one suite per error code reproducing its 07 "produced when" conditions; dedupe join + terminal-cache exclusions;
invocation-id-conflict-fails-closed(same key, different fingerprint) and cross-consumer id reuse; navigation-commit-then-owner-unmount; observation-bound overflow/starvation; fake-timer timeout with late-settlement audit; abort propagation; stale matrix (replaced / reloaded / version-mismatch-on-destructive). - Acceptance: 08 recipes that don't involve React/policies/confirmation all pass;
INVOCATION_CONFLICTbehavior (join vs conflict vs expiry) is an explicit acceptance criterion. - Depends: M3. Risks: the largest milestone — split into resolution/execution/dedupe PRs; race tests are the deliverable, not an afterthought.
M5 — Policy pipeline
- Scope:
AgentPolicytwo-stage API (D21): synconDiscoveryin snapshot + re-check at invoke, asynconAuthorizeonion (phase 4, no input) andonInvokeonion (phase 6, effective input only); chain composition (registry→component→capability); injectable clock in policy contexts; built-ins:authenticated,hasPermission,tenantBoundary,environment,rateLimit(authorize-stage),audit(invoke-stage); hide-vs-disable semantics (D11/D12). - Files:
core/src/policy.ts. - Tests: most-restrictive-wins; discovery/invocation consistency (hidden ⇒ NOT_FOUND, disabled ⇒ NOT_AVAILABLE); per-consumer filtering (requirement 12); each built-in.
- Acceptance: requirement-by-requirement checklist of 06 §mapped items 1–12 (minus confirmation, next).
- Depends: M4.
M6 — Confirmation + audit sinks
- Scope: pending store (bounded, D24), evidence lifecycle (bind/approve/deny/expire/consume; canonical request-digest binding with exact-value input match, D21),
ConfirmationController,requireConfirmationpolicy, TTL via injectable clock;AuditSink+ memory/console sinks; audit levels + redaction hook; harnessconfirmations.{approve,deny,expire}. - Files:
core/src/{confirmation,audit}.ts. - Tests: the full 06 §rules list — replay, mismatch (bait-and-switch), expiry, denial no-retry, staleness-beats-confirmation, pending re-request returns same id; audit event completeness.
- Acceptance: confirmation recipes in 08 pass verbatim.
- Depends: M5.
M7 — React package + React testing
- Scope:
AgentSurfaceProvider,useAgentSurface,useAgentComponent(commit-phase registration, identity keys, structural-fingerprint re-register, latest-ref handlers, availability push),usePendingConfirmations; ExperimentalAgentComponentScope/useAgentAction/useAgentObservation;renderAgentSurface+ matchers + semantic snapshots. - Files:
react/src/*;testing/src/{react,matchers,serialize}.ts. - Tests: Strict Mode symmetry; Suspense (register only after commit); identity-change re-register; handler freshness (state captured at invoke time); availability push on
whenflip; instance lists with entity ids; portal indifference; unmount-mid-invoke through real React; matcher semantics incl. hidden-vs-disabled; snapshot normalization (<reg#N>). - Acceptance: all React-flavored 08 recipes pass under React 18 + 19, Strict Mode on.
- Depends: M4 (invoke), M6 (confirmations) for full surface; can start on M2.
- Risks: highest subtlety — React 19 effect timing; mitigate by porting the exact semantics tests to both React majors from day one.
M8 — Embedded toolset adapter
- Scope:
createAgentToolset(direct mode), description templating (plane/effect prefixes), wire naming + per-version map, required topology declaration (D26: embedded→wait, remote→two-phase, neither→throw) + deterministic dispose of pending waits,subscribeon version change, tool-call-id → invocationId (consumer-namespaced, D22). - Files:
core/src/toolset.ts. - Tests: catalog refresh on version bump; wait-mode single-call confirmation round-trip (fake timers); two-phase relay; dedupe via toolCallId; wire-name overflow.
- Acceptance: a scripted fake "model" (list tools → call → handle error → retry per
retryhints) completes the devices scenario against a synthetic registry — no LLM. - Depends: M6. — v0.1 ships here.
M9 — oRPC package
- Scope:
createOrpcAgentBridge, manifest contract (OQ-1 escape hatch included), refs typing from router client,bindAgentProcedure/useAgentProcedure, binding semantics D7/D8 (schema surgery, locking, execution-timebind, full-schema re-parse), executor +callContext, exposure gating, suffix-collision feed,AMBIGUOUS_INSTANCEfor multi-reference. - Files:
orpc/src/{bridge,binding,executor}.ts,orpc/react.ts. - Tests: schema-surgery matrix (partial/all/none bound, required handling); locked override → INVALID_INPUT; overridable default behavior; binding throw → PRECONDITION_FAILED(binding-failed); manifest-absent ref registers nothing; executor receives merged validated input; server-error mapping incl. server-origin CONFIRMATION_REQUIRED.
- Acceptance: oRPC recipes in 08 + type-level tests (bind subset compile errors) pass.
- Depends: M8. Risks: orpc-agent API drift (OQ-1) — isolate behind the manifest type; only the bridge factory touches real orpc-agent.
M10 — Example application
- Scope:
examples/devices-app: the full 10-examples.md page (filters, table, drawer, disable flow, confirmation host, navigation component, two-instance page), mock oRPC backend, embedded fake-agent dev panel (scripted, optional real provider behind an env var — never in CI), the complete test suite from 08/10. - Acceptance: the 11-step scenario runs scripted end-to-end in CI (no LLM); every 08 recipe exists here at least once; semantic surface snapshot committed and reviewed.
- Depends: M9. Risks: none technical; this milestone exists to force spec honesty.
M11 — WebMCP adapter (Experimental)
- Scope:
@agent-surface/webmcpper 09 §webmcp: feature-detect, register/refresh on version change, wire naming, curation hook, two-phase confirmations. - Tests: against a
navigator.modelContextmock (the spec being unstable, the mock encodes our assumptions and is updated deliberately). - Acceptance: devices example exposes its surface through the mock; unavailable capabilities absent; drift risk documented in the package README.
- Depends: M8 (M10 useful, not required).
Cross-cutting definition of done (every milestone)
- No public API without spec text; no spec text contradicted by code (doc PRs ride along).
- Every typed error the milestone introduces is produced by at least one test.
environment: "development"diagnostics tested (they are API too).- Size-limit budgets updated consciously, never silently.
Risk register (top 5)
| Risk | Impact | Mitigation |
|---|---|---|
| React effect-timing semantics differ 18↔19 | wrong lifecycle = broken staleness story | dual-major CI from M7 day one; semantics tests, not implementation tests |
| JSON Schema subset too narrow for real apps | authors blocked at registration | fixture corpus grown from example app; D19 marked revisitable |
| orpc-agent API instability | M9 rework | manifest indirection (OQ-1); hand-written manifest path always works |
| Invocation pipeline race conditions | corrupted guarantees | M4 test-first on D13–D17; injectable clock; race tests in CI |
| Surface catalogs too big for model context in real apps | poor agent behavior | budgets + scoping shipped early (M3), meta-tools measured in v0.3 (OQ-4/OQ-9) |