Architecture

Canvas layer model and ownership boundaries.

Canvas is organized as an isolated agent workspace topology:

@agent-html/kernel
  -> framework-neutral protocol, policy, validation, diagnostics, and runtime catalog

@agent-html/react
  -> React rendering adapter and interaction provider

agent-html/*
  -> agent operating context and reusable resources

agent-html/artifacts/*
  -> durable artifact composition and content

packages/cli/src/*
  -> Vite middleware dev host, validation, and stylesheet entrypoints

packages/cli/src/host/*
  -> sandbox host, inspector, and prompt bridge

packages/cli/src/host/styles/*
  -> host chrome implementation styles that consume Canvas tokens

apps/desktop
  -> project selection, session state, runtime supervision, preferences,
     packaged Node, Desktop chrome, and the persisted Canvas theme snapshot

Each layer owns one decision. The protocol marks stable regions and emits instrumented state changes. The agent-html workspace provides durable context agents can read and reuse. Artifacts compose those resources into UI. The CLI discovers, validates, serves, and routes artifacts through Vite middleware. The host observes and operates on rendered metadata.

Kernel rules are non-overridable. All machine-verifiable protocol, dependency, workspace, and visual violations are blocking errors. Subjective composition judgment remains in TASTE.md and is not represented as a machine rule.

Desktop

apps/desktop is the packaged product shell. Its React renderer owns Workspace Home, recovery, switching, settings, and the compact frame around the existing Host. Its Tauri layer owns canonical project selection, recents, per-launch credentials, sidecar lifecycle, logs, capabilities, and packaging.

The selected project remains the source owner. Desktop normalizes either a project root or its agent-html/ directory to one canonical project root. Preferences and credentials never enter project source.

Canvas Style is the single theme authority. The Host publishes a versioned, allowlisted snapshot of its active mode, preset variables, and live draft to its Desktop parent. Desktop accepts messages only from the active runtime frame and origin, applies the snapshot to its own root, and persists the last valid snapshot globally for Workspace Home and future Canvas sessions. A Canvas host requests that snapshot before publishing its initial theme; Desktop sends it only to the active runtime frame and origin. Canvas validates and applies the bootstrap, then resumes publishing canonical theme changes. Standalone hosts retain local preference persistence.

Desktop resolves packaged runtime resources through a versioned manifest. Its runtime supervisor is the only owner of child-process path projection, command construction, output capture, readiness, exit classification, and shutdown. Canonical paths stay internal; child arguments receive platform-compatible paths at the process boundary. Workspace initialization is transactional and publishes agent-html/ only after the template copy completes.

Desktop Runtime is an immutable, content-addressed store under the app local data directory:

runtime/
  current.json
  runtimes/<fingerprint>/
    bin/node[.exe]
    node_modules/
    runtime-manifest.json

runtime:build publishes one verified fingerprint without selecting it. runtime:ensure single-flights the build, rechecks after the lock, and atomically selects it while retaining the previous fingerprint for recovery. runtime:start consumes only the selected manifest. runtime:stage copies the selected immutable version into the packaging seed and is forbidden from the development startup path. Cache hits do not run npm install; Canvas init never installs project dependencies.

The manifest owns Node and CLI entries, protocol and schema versions, the dependency-lock hash, Canvas dependency names, and Canvas browser entries. The fingerprint covers runtime source, dependency closure, Node binary, target, and the runtime build recipe. Rust passes its path and runtime fingerprint to the CLI. Vite keys optimized dependencies by runtime fingerprint, workspace root, and a browser optimization plan. That plan combines manifest-owned Canvas entries with Host entries derived from production Host source and disables undeclared dependency discovery. Runtime publication never replaces a directory used by a running sidecar.

The Rust supervisor owns the full runtime process tree. Windows assigns it to a kill-on-close Job Object. Unix launches it through a new-session supervisor whose control pipe kills the process group when Desktop disappears. Graceful shutdown remains the primary path; forced cleanup is bounded and tree-wide.

agent-html runtime is the controller contract. It binds a random loopback port, emits one JSON runtime-ready event, authenticates every request, exposes versioned health and shutdown routes, and closes Vite, registry watchers, and agent bridges before exit. agent-html dev remains the unauthenticated development contract. The packaged app supplies Node and CLI resources; projects do not supply a script, install, or runtime.

Protocol

Artifact and Block are headless protocol markers.

They own metadata, anchors, and accessibility minimums. Artifact also owns the readable root container, with layout parameters configured by Canvas CSS tokens instead of artifact source props. Block is fully protocol-only: it accepts id, title, and children. It does not own width, spacing, colors, borders, radius, shadow, cards, panels, grid structure, or props passthrough.

@agent-html/react exports InteractionProvider, useArtifactInteraction, useEmitArtifactStateChange, useInstrumentedValueChange, and useInstrumentedCheckedChange. These helpers record local control state and dispatch agent-html:state-change events.

Resources

Canvas resources live under agent-html:

AGENTS.md
package.json
components.json
tsconfig.json
styles/index.css
styles/base.css
styles/materials/*
styles/layouts/index.css
styles/layouts/composition.css
styles/internal/*
theme/*
components/ui/*
components/*.tsx
hooks/*
lib/*
types/*
assets/*
public/*
artifacts/*
artifacts/*/data/*

Artifacts should consume these resources before adding local one-off helpers.

Composition

Artifacts decide block content layout explicitly:

<Artifact title="Project Brief">
  <Block id="summary" title="Summary">
    <Card>...</Card>
  </Block>
</Artifact>

Block marks an inspectable region. It is not a card, panel, or section style. Do not put layout classes on Block; put layout inside the block content.

Host

The host renders artifacts, discovers blocks, displays validation diagnostics, overlays block prompt actions, handles floating prompts, applies theme presets, and loads Canvas stylesheets. Host controls use Canvas UI primitives and theme resources through #agent-html-playground/components/ui/* and #agent-html-playground/theme/*; the host must not define artifact visual language.

Host workbench code is a consumer and composer of runtime pipelines. Its owner map is:

host/api
  -> dev-server API contract

host/artifact
  -> artifact registry consumption and artifact module runtime

host/overlay
  -> rendered Block metadata measurement and prompt anchors

host/prompt
  -> floating prompt host state, drafts, and block message lifecycle

host/pipeline
  -> Codex/example backend adapters and prompt formatting calls

host/interaction
  -> artifact state-change event snapshots for prompt context

host/preferences
  -> local host preference persistence

host/theme
  -> host theme mode, preset layout, draft preview, and runtime variables

host/navigation
  -> sidebar command surface

ReactCanvasHostWorkbench composes those owners. It should not directly own dev-server route semantics, workspace validation, prompt backend execution, message store publishing, theme side effects, artifact registry refresh rules, or Canvas protocol semantics.

Pipeline Map

This page is the authoritative Canvas pipeline map. The shared block prompt model is:

artifactEntry + blockId + implementationPath + interactionSnapshot
PipelineOwnerInputOutputForbidden dependenciesPrimary tests
Workspace sourceagent-htmlartifact entries, split block files, local resourcesportable source workspacegenerated bundles, lockfiles, vendored installsworkspace-contract.test.mjs
Workspace file/path helpersreact-canvas/paths.mjs + react-canvas/workspace-file.mjsroot path or source file pathPOSIX workspace path or text sourcegeneric filesystem abstraction, mutation APIs, host statepipeline-ownership-contract.test.mjs, workspace-contract.test.mjs
Artifact discoveryreact-canvas/paths.mjsagent-html/artifacts/*.artifact.tsxartifact entry file pathsapp shell, runtime parser, host UIdev-host.test.mjs, pipeline-ownership-contract.test.mjs
Artifact entry validationKernel validator + CLI file adapterartifact entry sourceversioned blocking diagnosticsduplicated protocol parsing in CLI or HostKernel validation tests, validation.test.mjs
Block implementation validationKernel validator + CLI file adapteragent-html/artifacts/**/*.block.tsxversioned blocking diagnosticsraw visual classes, native primitive bypassesKernel validation tests, validation.test.mjs, style-ownership-contract.test.mjs
Workspace boundary validationKernel validator + react-canvas/validation.mjsartifact and block source importsworkspace diagnosticsapplication imports, public-file importsvalidation.test.mjs, workspace-contract.test.mjs, architecture-boundaries.test.mjs
Dependency ownershipKernel runtime catalog + generated manifestsCanvas source package importone CLI-supplied versionhand-maintained workspace dependency versionsKernel policy tests, package-runtime-contract.test.mjs
Dependency resolutiondev-server/vite.mjssource import specifierCLI-provided runtime moduleagent-html/node_modules, source-owned runtime installsruntime-resolution-contract.test.mjs
Module transform and renderVite middleware + host artifact runtimeartifact entry file pathmounted artifact DOM with HMR-capable module graphapplication shell and Host internalsdev-host.test.mjs, artifact-runtime.test.ts
Public/static asset servingdev-server/routes.mjs + host/api URL helperspublic asset path or proxied font URLserved asset response or host URLdirect Host endpoint strings outside host/apidev-host.test.mjs, routes.test.mjs, api.test.ts, architecture-boundaries.test.mjs
Artifact registry consumptionhost/artifact/use-artifact-registry.tsartifact registry response and pending artifact pathactive artifact, diagnostics, registry refresh operationsdev-server route implementation, workspace validationapp.test.tsx, pipeline-ownership-contract.test.mjs
Block overlaypackages/react + host overlayrendered data-agent-html-block-* metadatahover highlight and prompt targetartifact source mutation, host APIs in artifactsblock-overlay.test.ts, index.test.tsx
Block implementation lookupdev server + host APIartifactEntry, blockIdimplementationPathselected source payloads, implementation source payloadsblock-implementation.test.mjs, dev-host.test.mjs
Block promptreact-canvas/prompt.mjsartifactEntry, blockId, optional implementationPath, host interactionSnapshotminimal prompt packetselected source payloads, implementation source payloadsprompt.test.mjs
Create artifacthost/pipeline + react-canvas/prompt.mjscreate request and generated artifact file pathCodex/example turn requestdev-server implementation, artifact source mutation in Hostpipeline.test.ts, app.test.tsx
Validation fixhost/pipeline + react-canvas/prompt.mjsblocking diagnostics for one artifactCodex/example turn requestwarnings-only prompt payloads, host UI state payloadspipeline.test.ts, prompt.test.mjs
Interaction statepackages/react event protocol + host storeartifact-local control changeshost prompt interaction snapshotfilesystem, shell, MCP, privileged host APIs in artifact codeindex.test.tsx, interaction-store.test.ts, prompt.test.mjs
Preferences persistencehost/preferenceslocal host preference statepersisted host preference patchartifact source state, dev-server statecanvas-host-preferences.test.ts, use-canvas-host-preferences.test.ts
CSS/themeagent-html/styles + package host styles + host theme ownerCSS tokens, content classes, theme presets, host chrome CSSstylesheet and runtime variablesartifact-owned root chrome, sidebar token overrides, host chrome implementation in artifact contextstyle-ownership-contract.test.mjs, style-resource-contract.test.mjs, theme-preset.test.ts, theme-layout.test.ts
Runtime controldev-server/runtime-session.mjs + runtime-command.mjsper-launch credential, canonical root, pipelineready event, authenticated Host, health, graceful shutdownpersisted credentials, fixed ports, project Noderuntime-session.test.mjs, runtime-server.test.mjs
Desktop sessionapps/desktop/src-tauri + apps/desktop/src/session.tsselected folder, initialization choice, runtime manifest, preferencesone supervised workspace runtime and typed recovery errorsartifact layout, Host route semantics, project preferences, multiple active runtimesDesktop Vitest, Cargo tests, runtime smoke, packaged E2E
Desktop visual ownershipapps/desktop/src/styles.css + Desktop primitivessemantic and --canvas-desktop-* tokensborderless product chromeworkspace UI imports, raw colors, palette utilities, local visual scalesapps/desktop/src/style-ownership.test.ts

Dev server routes are orchestration only. They are grouped into explicit runtime adapter pipelines:

host-shell
runtime-module
styles-and-assets
public-asset
artifact-registry-and-validation-report
artifact-source-mutation
block-lookup
codex-bridge
runtime-control

The dev server owns server lifecycle, route dispatch, Vite runtime creation, dependency supply, and asset serving. Host shell, artifact module, stylesheet, public asset, artifact registry and validation report, artifact mutation, block lookup, and Codex bridge requests enter through those named pipelines. The dev server delegates host and artifact module transforms to Vite.

It does not own protocol semantics, host interaction state, artifact visual language, or block prompt meaning. Those remain owned by @agent-html/react, the host interaction store, Canvas styles/resources, and the prompt pipeline.

Boundary Governance

Canvas boundary checks follow the layer topology:

packages/kernel
  -> framework-neutral contract and policy; no React, filesystem, Host,
     workspace, docs, app, or archive imports

packages/react
  -> React adapter only; imports Kernel but no host, workspace, docs, app,
     or archive code

packages/cli/src/react-canvas
  -> discovery, validation, prompt, and workspace path orchestration
  -> no host UI, artifact source, Canvas UI, theme, docs, app, or archive imports

packages/cli/src/host
  -> host inspector chrome and pipeline calls
  -> Canvas UI/theme only through #agent-html-playground/components/ui/* and
     #agent-html-playground/theme/*

agent-html/artifacts
  -> artifact source
  -> no host, app, docs, archive, public-file imports, or internal style imports

packages/cli/src/react-canvas/architecture-boundaries.test.mjs owns the topology checks. Use it as the executable boundary matrix before adding new validation behavior. Workspace source constraints live in workspace-contract.test.mjs, package/runtime ownership lives in package-runtime-contract.test.mjs, pipeline ownership lives in pipeline-ownership-contract.test.mjs, archive separation lives in archive-runtime-boundary-contract.test.mjs, and style ownership lives in style-ownership-contract.test.mjs plus style-resource-contract.test.mjs.

style-ownership-contract.test.mjs also records the current fixed-format artifact layout exceptions for media, map, and carousel blocks. Those exceptions are not a new styling surface; new artifact code should prefer Canvas public composition classes and shrink that list when equivalent composition classes exist.

Dependency Resolution

agent-html/package.json records source dependency metadata for local Canvas resources. It is not an installation target. The CLI package provides runtime dependencies. The dev host is the only mapping layer between source imports and runtime modules: it resolves workspace imports through CLI-owned dependencies, exact package aliases, Vite import-entry resolution, and explicit optimizeDeps.

React and React DOM stay pinned through dedicated aliases so the host and artifacts share one renderer instance. Named exports used by workspace source must be present on the resolved runtime entries; this catches ESM/CJS mismatch before browser module loading fails.

Host browser dependencies are a separate CLI-owned contract. The dev server derives its entries from production Host imports, requires their packages in the CLI production manifest, and combines them with Canvas entries only for Vite optimization. Host entries do not expand the dependency surface available to Canvas source. The combined plan digest owns cache identity and completeness, so changing an entry cannot reuse a cache prepared for a different browser graph.

The dependency contract is executable: workspace-contract.test.mjs checks source-only workspace files, source metadata, and forbidden vendored installs. package-runtime-contract.test.mjs checks CLI runtime ownership. runtime-resolution-contract.test.mjs checks Vite dependency aliases, ESM import entries, runtime exports, React singleton resolution, explicit optimizeDeps, and filesystem allow-list boundaries. routes.test.mjs checks route pipeline ownership. dev-host.test.mjs checks artifact rendering and fresh-project startup through the real dev host.

Diagnostic Categories

Canvas validation returns one versioned diagnostic list. protocol, style, and workspace diagnostics are produced from source; dependency and manifest diagnostics enforce the Kernel runtime catalog. Every diagnostic is blocking and identifies its code, file, location, message, policy version, and optional fix suggestion. The dev host displays the same contract returned by the CLI validation pipeline.

Interaction Source Of Truth

@agent-html/react owns portable instrumentation and the event protocol. Its artifact-local snapshot helps controls compose locally, but it is not the prompt context source of truth.

The host interaction store owns prompt context. It records agent-html:state-change events by artifactEntry + blockId, compacts them, and passes the resulting interactionSnapshot into the block prompt pipeline.

On this page