{
  "$schema": "https://dgadsuq3fvhh4.cloudfront.net/docs/agent/schema/index.schema.json",
  "schemaVersion": 2,
  "manifestSchemaVersion": 1,
  "generatedAt": "2026-10-20T05:44:25.751Z",
  "sourceRevision": "cdbb9388980b61e901885b57e006eddae4e168fb",
  "enginePackageName": "@cyberart-io/engine",
  "enginePackageVersion": "0.0.12",
  "framework": {
    "name": "Cyberart",
    "enginePackageName": "@cyberart-io/engine",
    "enginePackageVersion": "0.0.12",
    "siteOrigin": "https://dgadsuq3fvhh4.cloudfront.net",
    "summary": "Explain host, runtime, and cart: the host owns the page and capabilities; the runtime mounts a cart; the cart is a state machine that optionally paints."
  },
  "routes": {
    "landingRoute": "/framework",
    "docsRoot": "/docs",
    "gettingStartedRoute": "/docs/getting-started",
    "featureIndexRoute": "/docs/features",
    "featureRoutePattern": "/docs/features/:slug",
    "exampleRoutePattern": "/docs/examples/:exampleId",
    "agentIndexRoute": "/docs/agent/index.json",
    "agentFeatureRoutePattern": "/docs/agent/features/:slug.json",
    "agentMarkdownRoute": "/docs/agent/index.md",
    "agentIndexSchemaRoute": "/docs/agent/schema/index.schema.json",
    "agentFeatureSchemaRoute": "/docs/agent/schema/feature.schema.json",
    "llmsTxtRoute": "/llms.txt",
    "sitemapRoute": "/sitemap.xml",
    "robotsRoute": "/robots.txt"
  },
  "sectionKinds": {
    "normative": "Framework contract. Hosts and carts may rely on it for the compatible engine versions listed.",
    "tutorial": "Explanation and guidance. Not a contract.",
    "example": "Behaviour of one canonical example at its pinned commit. Not a framework contract.",
    "provenance": "Versions, source paths, tests, and links. Use to pin or verify."
  },
  "sections": [
    {
      "id": "problem",
      "anchor": "problem",
      "heading": "What this solves",
      "kind": "tutorial",
      "required": true,
      "purpose": "Name the problem the feature exists to solve."
    },
    {
      "id": "when-to-use",
      "anchor": "when-to-use",
      "heading": "When to use it",
      "kind": "tutorial",
      "required": true,
      "purpose": "Say when this is the right tool, and when a related feature is a better fit."
    },
    {
      "id": "conceptual-model",
      "anchor": "conceptual-model",
      "heading": "Conceptual model",
      "kind": "tutorial",
      "required": true,
      "purpose": "Objects, states, and how they relate. Diagrams optional; names must match the API."
    },
    {
      "id": "lifecycle",
      "anchor": "lifecycle",
      "heading": "Lifecycle",
      "kind": "normative",
      "required": true,
      "purpose": "Create / attach / run / snapshot / restore / destroy, including which events fire."
    },
    {
      "id": "cart-vs-host",
      "anchor": "cart-vs-host",
      "heading": "Cart versus host",
      "kind": "normative",
      "required": true,
      "purpose": "What the cart may declare or emit versus what the host/runtime must provide. Do not blur the boundary."
    },
    {
      "id": "contract",
      "anchor": "contract",
      "heading": "Minimal contract",
      "kind": "normative",
      "required": true,
      "purpose": "Smallest API/configuration surface that a correct integration uses, with types and defaults."
    },
    {
      "id": "failure",
      "anchor": "failure",
      "heading": "Failure and fallback",
      "kind": "normative",
      "required": true,
      "purpose": "How the feature fails closed, what events/diagnostics look like, and what the host should do."
    },
    {
      "id": "accessibility",
      "anchor": "accessibility",
      "heading": "Accessibility",
      "kind": "normative",
      "required": true,
      "purpose": "Keyboard, focus, reduced motion, captions/non-audio alternatives, contrast, and anything the feature itself publishes (e.g. semantic regions)."
    },
    {
      "id": "example",
      "anchor": "example",
      "heading": "Runnable example",
      "kind": "example",
      "required": true,
      "purpose": "Link canonical example IDs from the manifest. Embed and exact source come from the lock; this section must not paste a drifting snippet."
    },
    {
      "id": "expected-result",
      "anchor": "expected-result",
      "heading": "Expected result",
      "kind": "example",
      "required": true,
      "purpose": "What a reader should see, hear, or be able to do after the example runs."
    },
    {
      "id": "source",
      "anchor": "source",
      "heading": "Source, tests, and version",
      "kind": "provenance",
      "required": true,
      "purpose": "Links to implementationPath, tests, engine guide, and the pinned enginePackageVersion."
    },
    {
      "id": "related",
      "anchor": "related",
      "heading": "Related features",
      "kind": "tutorial",
      "required": true,
      "purpose": "Prerequisites first, then relatedFeatureIds. Use manifest ids, not free text names."
    },
    {
      "id": "advanced",
      "anchor": "advanced",
      "heading": "Advanced composition",
      "kind": "tutorial",
      "required": false,
      "purpose": "Optional: combining this feature with runtime groups, compositor, bindings, or scenarios."
    },
    {
      "id": "version",
      "anchor": "version",
      "heading": "Version provenance",
      "kind": "provenance",
      "required": true,
      "purpose": "Engine package version this page describes, when the API first appeared, and a warning if the live package.json has moved on."
    }
  ],
  "features": [
    {
      "id": "runtime",
      "slug": "runtime",
      "route": "/docs/features/runtime",
      "title": "Runtime and carts",
      "purpose": "Mount an AnimationCart into a host-owned DOM element, run the update/render loop, and expose pause, snapshot, events, and teardown.",
      "status": "substantial",
      "navGroup": "core",
      "packageName": "@cyberart-io/engine",
      "enginePackageVersion": "0.0.12",
      "compatibleEngineVersions": {
        "min": "0.0.6"
      },
      "documentationSource": {
        "kind": "site-page",
        "path": "src/docs/pages/runtime.ts"
      },
      "exampleIds": [
        "create-runtime-canvas"
      ],
      "relatedFeatureIds": [
        "random",
        "keyboard",
        "pointer",
        "events",
        "deterministic-mode",
        "calculation-carts",
        "snapshots"
      ],
      "prerequisiteFeatureIds": [],
      "aliases": [],
      "keywords": [
        "createRuntime",
        "AnimationCart",
        "mount",
        "cart",
        "HostChannel"
      ],
      "surfaces": [
        "browser"
      ],
      "primaryExports": [
        "createRuntime",
        "HostChannel"
      ],
      "agentRoute": "/docs/agent/features/runtime.json",
      "summary": "Mount an AnimationCart into a host-owned DOM element, run the update/render loop, and expose pause, snapshot, events, and teardown.",
      "owningModule": "./canvas/cyberArtRuntime",
      "lifecycle": {
        "status": "substantial",
        "introducedIn": "0.0.6",
        "compatibleEngineVersions": {
          "min": "0.0.6"
        },
        "normative": true,
        "experimental": false,
        "deprecated": false,
        "successorFeatureIds": []
      },
      "evidence": {
        "implementationPath": "packages/engine/src/canvas/cyberArtRuntime.ts",
        "tests": [
          "packages/engine/src/canvas/cyberArtRuntime.spec.ts",
          "packages/engine/src/canvas/cyb-57-exports.repro.spec.ts"
        ]
      },
      "redirectsFrom": [],
      "examples": [
        {
          "id": "create-runtime-canvas",
          "route": "/docs/examples/create-runtime-canvas",
          "title": "Silent createRuntime canvas fill",
          "purpose": "Show the engine README quick start: mount a silent cart, fill the canvas from seed-driven state, and start the loop. No audio, network, or extra UI.",
          "featureIds": [
            "runtime"
          ],
          "runtimeKind": "render",
          "artifact": {
            "state": "available",
            "reason": "Cache verified against the lock."
          },
          "source": {
            "state": "restricted",
            "reason": "Source not published; example-carts is private.",
            "files": [
              "cart.ts",
              "main.ts",
              "manifest.json"
            ]
          },
          "provenance": {
            "repository": "cyberart-io/example-carts",
            "commit": "2ed0eb599ded827cca46bcd2fc66dea78f387a0c",
            "engineVersion": "0.0.12",
            "sourceUrl": null
          },
          "capabilities": {
            "permissions": [],
            "assets": [],
            "network": "none",
            "credentials": "none"
          },
          "interaction": {
            "mode": "none",
            "notes": "No pointer or keyboard. The cart starts immediately because it is silent."
          },
          "expected": {
            "visible": "The canvas is filled with an rgb color whose red channel is taken from the token seed hue and drifts each frame.",
            "audible": "silent"
          },
          "accessibility": {
            "notes": "No interactive controls. Docs embeds should caption the canvas as a seed-colored fill.",
            "fallback": "A text description of a solid color field that slowly changes is sufficient when canvas is unavailable."
          },
          "viewport": {
            "width": 320,
            "height": 180
          }
        }
      ],
      "pageSections": [
        {
          "id": "problem",
          "anchor": "problem",
          "heading": "What this solves",
          "kind": "tutorial",
          "required": true,
          "purpose": "Name the problem the feature exists to solve.",
          "text": "A host page needs to mount an AnimationCart into a DOM element it owns, run update/render, and expose pause, snapshot, events, and teardown without the cart owning the document."
        },
        {
          "id": "when-to-use",
          "anchor": "when-to-use",
          "heading": "When to use it",
          "kind": "tutorial",
          "required": true,
          "purpose": "Say when this is the right tool, and when a related feature is a better fit.",
          "text": "Use createRuntime whenever one cart should paint or calculate inside a container you provide. Use createRuntimeGroup when several carts must share a router and lockstep clock, and do not call createRuntime from a cart."
        },
        {
          "id": "conceptual-model",
          "anchor": "conceptual-model",
          "heading": "Conceptual model",
          "kind": "tutorial",
          "required": true,
          "purpose": "Objects, states, and how they relate. Diagrams optional; names must match the API.",
          "text": "The host owns the page and the container. createRuntime returns a CyberArtRuntime: mount(cart) prepares getDefaultState (and throws if prepare fails) but does not start the loop; CartHandle.start begins it, destroy unloads the cart, and a second mount unloads the first — one cart per runtime."
        },
        {
          "id": "lifecycle",
          "anchor": "lifecycle",
          "heading": "Lifecycle",
          "kind": "normative",
          "required": true,
          "purpose": "Create / attach / run / snapshot / restore / destroy, including which events fire.",
          "text": "createRuntime({ container }) → mount(cart) → start() → pause/resume/step → exportState/importState or exportSnapshot/importSnapshot → destroy the handle, then runtime.destroy(). pagehide is the usual teardown hook; silent carts may start() without a click."
        },
        {
          "id": "cart-vs-host",
          "anchor": "cart-vs-host",
          "heading": "Cart versus host",
          "kind": "normative",
          "required": true,
          "purpose": "What the cart may declare or emit versus what the host/runtime must provide. Do not blur the boundary.",
          "text": "The cart is a state machine: getDefaultState, update, optional render. The host owns the DOM node, seed, capabilities, audio unlock, and persistence; the runtime never fetches and never creates a second document."
        },
        {
          "id": "contract",
          "anchor": "contract",
          "heading": "Minimal contract",
          "kind": "normative",
          "required": true,
          "purpose": "Smallest API/configuration surface that a correct integration uses, with types and defaults.",
          "text": "Smallest host:\n\n```ts\nimport { createRuntime } from \"@cyberart-io/engine\";\n\nconst hostRuntime = createRuntime({ container: stage });\nconst handle = hostRuntime.mount(cart);\nawait handle.start();\nhandle.destroy();\nhostRuntime.destroy();\n```\n\ncreateRuntime and mount are synchronous. Pass seed for generative output. kind: 'calculation' skips the canvas. captureKeyboard defaults false in embeds."
        },
        {
          "id": "failure",
          "anchor": "failure",
          "heading": "Failure and fallback",
          "kind": "normative",
          "required": true,
          "purpose": "How the feature fails closed, what events/diagnostics look like, and what the host should do.",
          "text": "mount throws if prepare fails. runtime.onError receives frame errors with phase, consecutive, and stopped. A second mount unloads the first; destroy is idempotent. Do not start() a Tone cart outside a user gesture — the loop may never begin."
        },
        {
          "id": "accessibility",
          "anchor": "accessibility",
          "heading": "Accessibility",
          "kind": "normative",
          "required": true,
          "purpose": "Keyboard, focus, reduced motion, captions/non-audio alternatives, contrast, and anything the feature itself publishes (e.g. semantic regions).",
          "text": "The host supplies the container and any click-to-start overlay (needsAudio). This feature does not publish semantic regions; pair with semantic-layers when the canvas is the only UI, and caption silent fills in docs embeds."
        },
        {
          "id": "example",
          "anchor": "example",
          "heading": "Runnable example",
          "kind": "example",
          "required": true,
          "purpose": "Link canonical example IDs from the manifest. Embed and exact source come from the lock; this section must not paste a drifting snippet.",
          "text": "The embed below is the pinned create-runtime-canvas cart. Source lives on the example page, not here."
        },
        {
          "id": "expected-result",
          "anchor": "expected-result",
          "heading": "Expected result",
          "kind": "example",
          "required": true,
          "purpose": "What a reader should see, hear, or be able to do after the example runs.",
          "text": "After Run, the canvas fills with a seed-driven rgb color that drifts each frame. No audio. Reset remounts with the same docs seed."
        },
        {
          "id": "source",
          "anchor": "source",
          "heading": "Source, tests, and version",
          "kind": "provenance",
          "required": true,
          "purpose": "Links to implementationPath, tests, engine guide, and the pinned enginePackageVersion.",
          "text": "Implementation packages/engine/src/canvas/cyberArtRuntime.ts. Tests: packages/engine/src/canvas/cyberArtRuntime.spec.ts and packages/engine/src/canvas/cyb-57-exports.repro.spec.ts. Host API: packages/engine/README.md (Quick start / Host API)."
        },
        {
          "id": "related",
          "anchor": "related",
          "heading": "Related features",
          "kind": "tutorial",
          "required": true,
          "purpose": "Prerequisites first, then relatedFeatureIds. Use manifest ids, not free text names.",
          "text": "Related: random, keyboard, pointer, events, deterministic-mode, calculation-carts, snapshots. Prerequisites: none."
        },
        {
          "id": "advanced",
          "anchor": "advanced",
          "heading": "Advanced composition",
          "kind": "tutorial",
          "required": false,
          "purpose": "Optional: combining this feature with runtime groups, compositor, bindings, or scenarios.",
          "text": "Several pieces on one page: several createRuntime calls, or one createRuntimeGroup. Calculation participants share the group without a canvas."
        },
        {
          "id": "version",
          "anchor": "version",
          "heading": "Version provenance",
          "kind": "provenance",
          "required": true,
          "purpose": "Engine package version this page describes, when the API first appeared, and a warning if the live package.json has moved on.",
          "text": "Ships in @cyberart-io/engine 0.0.9. First appeared in 0.0.6."
        }
      ]
    },
    {
      "id": "random",
      "slug": "random",
      "route": "/docs/features/random",
      "title": "Seeded random",
      "purpose": "Art Blocks / kautomata sfc32 Random plus token seed helpers so generative output follows the hash.",
      "status": "substantial",
      "navGroup": "core",
      "packageName": "@cyberart-io/engine",
      "enginePackageVersion": "0.0.12",
      "compatibleEngineVersions": {
        "min": "0.0.6"
      },
      "documentationSource": {
        "kind": "site-page",
        "path": "src/docs/pages/random.ts"
      },
      "exampleIds": [],
      "relatedFeatureIds": [
        "runtime",
        "snapshots"
      ],
      "prerequisiteFeatureIds": [],
      "aliases": [],
      "keywords": [
        "random",
        "Random",
        "canonicalizeSeed",
        "resolveRuntimeSeed"
      ],
      "surfaces": [
        "browser"
      ],
      "primaryExports": [
        "Random",
        "canonicalizeSeed",
        "resolveRuntimeSeed"
      ],
      "agentRoute": "/docs/agent/features/random.json",
      "summary": "Art Blocks / kautomata sfc32 Random plus token seed helpers so generative output follows the hash.",
      "owningModule": "./generative/random",
      "lifecycle": {
        "status": "substantial",
        "introducedIn": "0.0.6",
        "compatibleEngineVersions": {
          "min": "0.0.6"
        },
        "normative": true,
        "experimental": false,
        "deprecated": false,
        "successorFeatureIds": []
      },
      "evidence": {
        "implementationPath": "packages/engine/src/generative/random.ts",
        "tests": [
          "packages/engine/src/generative/random.spec.ts"
        ]
      },
      "redirectsFrom": [],
      "examples": [],
      "pageSections": [
        {
          "id": "problem",
          "anchor": "problem",
          "heading": "What this solves",
          "kind": "tutorial",
          "required": true,
          "purpose": "Name the problem the feature exists to solve.",
          "text": "Generative output must follow the token hash. Math.random() and Date.now() in cart logic break replay."
        },
        {
          "id": "when-to-use",
          "anchor": "when-to-use",
          "heading": "When to use it",
          "kind": "tutorial",
          "required": true,
          "purpose": "Say when this is the right tool, and when a related feature is a better fit.",
          "text": "Use Random from getDefaultState/update/render, and canonicalizeSeed / resolveRuntimeSeed when the host accepts ?hash=. Snapshots record rng; importState still does not replay the PRNG."
        },
        {
          "id": "conceptual-model",
          "anchor": "conceptual-model",
          "heading": "Conceptual model",
          "kind": "tutorial",
          "required": true,
          "purpose": "Objects, states, and how they relate. Diagrams optional; names must match the API.",
          "text": "Art Blocks / kautomata sfc32 Random: the same hash yields the same sequence after warmup. A 64-hex hash is used as-is (optional 0x); canonicalizeSeed mixes any other value into 64-hex; metadata.generative: true refuses load onto a different hash."
        },
        {
          "id": "lifecycle",
          "anchor": "lifecycle",
          "heading": "Lifecycle",
          "kind": "normative",
          "required": true,
          "purpose": "Create / attach / run / snapshot / restore / destroy, including which events fire.",
          "text": "createRuntime({ seed }) → Random in callbacks follows that hash → Random.getState()/setState() snapshot the dual generators. setState throws if the snapshot seed does not match."
        },
        {
          "id": "cart-vs-host",
          "anchor": "cart-vs-host",
          "heading": "Cart versus host",
          "kind": "normative",
          "required": true,
          "purpose": "What the cart may declare or emit versus what the host/runtime must provide. Do not blur the boundary.",
          "text": "The host passes seed. The cart uses Random and must not call Math.random(). Live mode may keep a short ?hash= string; deterministic mode always canonicalizes (resolveRuntimeSeed)."
        },
        {
          "id": "contract",
          "anchor": "contract",
          "heading": "Minimal contract",
          "kind": "normative",
          "required": true,
          "purpose": "Smallest API/configuration surface that a correct integration uses, with types and defaults.",
          "text": "```ts\ngetDefaultState(R) {\n  return { hue: R.dec(0, 360), t: 0 };\n}\n```\n\nPrimary exports: Random, canonicalizeSeed, resolveRuntimeSeed."
        },
        {
          "id": "failure",
          "anchor": "failure",
          "heading": "Failure and fallback",
          "kind": "normative",
          "required": true,
          "purpose": "How the feature fails closed, what events/diagnostics look like, and what the host should do.",
          "text": "setState throws on seed mismatch. Generative carts refuse a save from another hash. Do not use wall clocks in cart logic if you need replay."
        },
        {
          "id": "accessibility",
          "anchor": "accessibility",
          "heading": "Accessibility",
          "kind": "normative",
          "required": true,
          "purpose": "Keyboard, focus, reduced motion, captions/non-audio alternatives, contrast, and anything the feature itself publishes (e.g. semantic regions).",
          "text": "Seed is not a user-facing control unless the host exposes it. Caption seed-driven visuals; do not rely on color alone if the seed is the content."
        },
        {
          "id": "example",
          "anchor": "example",
          "heading": "Runnable example",
          "kind": "example",
          "required": true,
          "purpose": "Link canonical example IDs from the manifest. Embed and exact source come from the lock; this section must not paste a drifting snippet.",
          "text": "No canonical example yet. packages/engine/README.md (Write a cart) and packages/engine/src/generative/random.spec.ts are the runtime proof."
        },
        {
          "id": "expected-result",
          "anchor": "expected-result",
          "heading": "Expected result",
          "kind": "example",
          "required": true,
          "purpose": "What a reader should see, hear, or be able to do after the example runs.",
          "text": "The same seed produces the same Random sequence. The bound runtime example’s hue is a function of the docs seed."
        },
        {
          "id": "source",
          "anchor": "source",
          "heading": "Source, tests, and version",
          "kind": "provenance",
          "required": true,
          "purpose": "Links to implementationPath, tests, engine guide, and the pinned enginePackageVersion.",
          "text": "Implementation packages/engine/src/generative/random.ts. Tests: packages/engine/src/generative/random.spec.ts. README: Write a cart."
        },
        {
          "id": "related",
          "anchor": "related",
          "heading": "Related features",
          "kind": "tutorial",
          "required": true,
          "purpose": "Prerequisites first, then relatedFeatureIds. Use manifest ids, not free text names.",
          "text": "Related: runtime, snapshots. Prerequisites: none."
        },
        {
          "id": "advanced",
          "anchor": "advanced",
          "heading": "Advanced composition",
          "kind": "tutorial",
          "required": false,
          "purpose": "Optional: combining this feature with runtime groups, compositor, bindings, or scenarios.",
          "text": ""
        },
        {
          "id": "version",
          "anchor": "version",
          "heading": "Version provenance",
          "kind": "provenance",
          "required": true,
          "purpose": "Engine package version this page describes, when the API first appeared, and a warning if the live package.json has moved on.",
          "text": "Ships in @cyberart-io/engine 0.0.9. First appeared in 0.0.6."
        }
      ]
    },
    {
      "id": "keyboard",
      "slug": "keyboard",
      "route": "/docs/features/keyboard",
      "title": "Keyboard manager",
      "purpose": "Cart-facing single-character key bindings with optional window capture and deterministic inject().",
      "status": "substantial",
      "navGroup": "input",
      "packageName": "@cyberart-io/engine",
      "enginePackageVersion": "0.0.12",
      "compatibleEngineVersions": {
        "min": "0.0.6"
      },
      "documentationSource": {
        "kind": "site-page",
        "path": "src/docs/pages/keyboard.ts"
      },
      "exampleIds": [
        "keyboard-inject"
      ],
      "relatedFeatureIds": [
        "pointer",
        "midi",
        "runtime",
        "cart-state"
      ],
      "prerequisiteFeatureIds": [],
      "aliases": [],
      "keywords": [
        "keyboard",
        "KeyboardManager"
      ],
      "surfaces": [
        "browser"
      ],
      "primaryExports": [
        "KeyboardManager"
      ],
      "agentRoute": "/docs/agent/features/keyboard.json",
      "summary": "Cart-facing single-character key bindings with optional window capture and deterministic inject().",
      "owningModule": "./keyboardManager",
      "lifecycle": {
        "status": "substantial",
        "introducedIn": "0.0.6",
        "compatibleEngineVersions": {
          "min": "0.0.6"
        },
        "normative": true,
        "experimental": false,
        "deprecated": false,
        "successorFeatureIds": []
      },
      "evidence": {
        "implementationPath": "packages/engine/src/keyboardManager.ts",
        "tests": [
          "packages/engine/src/canvas/cyberArtRuntime.spec.ts"
        ]
      },
      "redirectsFrom": [],
      "examples": [
        {
          "id": "keyboard-inject",
          "route": "/docs/examples/keyboard-inject",
          "title": "Keyboard inject and key trace",
          "purpose": "Show KeyboardManager: register a single-character action, inject or press j, and paint a growing bar plus last-key fill. No audio, network, or credentials.",
          "featureIds": [
            "keyboard"
          ],
          "runtimeKind": "render",
          "artifact": {
            "state": "available",
            "reason": "Cache verified against the lock."
          },
          "source": {
            "state": "restricted",
            "reason": "Source not published; example-carts is private.",
            "files": [
              "cart.ts",
              "main.ts",
              "manifest.json"
            ]
          },
          "provenance": {
            "repository": "cyberart-io/example-carts",
            "commit": "2ed0eb599ded827cca46bcd2fc66dea78f387a0c",
            "engineVersion": "0.0.12",
            "sourceUrl": null
          },
          "capabilities": {
            "permissions": [],
            "assets": [],
            "network": "none",
            "credentials": "none"
          },
          "interaction": {
            "mode": "keyboard",
            "notes": "Press j after Run. Headless tests call harness.key('j')."
          },
          "expected": {
            "visible": "A light bar grows across a dark canvas each time j is received. After the first j the background shifts to blue-gray.",
            "audible": "silent"
          },
          "accessibility": {
            "notes": "The initiating key is j. Docs should say so next to the embed. The bar width is the hit count.",
            "fallback": "Text: hits increment when j is injected; lastKey becomes j."
          },
          "viewport": {
            "width": 320,
            "height": 180
          }
        }
      ],
      "pageSections": [
        {
          "id": "problem",
          "anchor": "problem",
          "heading": "What this solves",
          "kind": "tutorial",
          "required": true,
          "purpose": "Name the problem the feature exists to solve.",
          "text": "Carts need single-character key bindings without owning window, and a deterministic host needs inject() so CI can drive keys without a real keyboard."
        },
        {
          "id": "when-to-use",
          "anchor": "when-to-use",
          "heading": "When to use it",
          "kind": "tutorial",
          "required": true,
          "purpose": "Say when this is the right tool, and when a related feature is a better fit.",
          "text": "Register one-character actions on the KeyboardManager the runtime passes into getDefaultState/update. Use pointer for pointer position/buttons and midi for notes/CC; do not add window keydown listeners inside a cart."
        },
        {
          "id": "conceptual-model",
          "anchor": "conceptual-model",
          "heading": "Conceptual model",
          "kind": "tutorial",
          "required": true,
          "purpose": "Objects, states, and how they relate. Diagrams optional; names must match the API.",
          "text": "KeyboardManager maps a single character to a handler. Full-page players pass captureKeyboard: true (listen on window); docs embeds default false so the page keeps its keys. inject(key) runs the same handler without a DOM event."
        },
        {
          "id": "lifecycle",
          "anchor": "lifecycle",
          "heading": "Lifecycle",
          "kind": "normative",
          "required": true,
          "purpose": "Create / attach / run / snapshot / restore / destroy, including which events fire.",
          "text": "The runtime constructs KeyboardManager at prepare. Register actions in getDefaultState; destroy() removes the window listener and clears the map so reloads do not accumulate handlers. inject may be called from tests or schedule({ type: \"key\" })."
        },
        {
          "id": "cart-vs-host",
          "anchor": "cart-vs-host",
          "heading": "Cart versus host",
          "kind": "normative",
          "required": true,
          "purpose": "What the cart may declare or emit versus what the host/runtime must provide. Do not blur the boundary.",
          "text": "The cart registers handlers and mutates its state from them. The host chooses captureKeyboard. Deterministic hosts call inject or schedule key actions; they do not synthesize KeyboardEvents unless they want to."
        },
        {
          "id": "contract",
          "anchor": "contract",
          "heading": "Minimal contract",
          "kind": "normative",
          "required": true,
          "purpose": "Smallest API/configuration surface that a correct integration uses, with types and defaults.",
          "text": "Only single-character keys may be registered.\n\n```ts\ngetDefaultState(R, dim, params, keys) {\n  const state = { hits: 0, lastKey: \"\" };\n  keys.registerAction(\"j\", () => {\n    state.hits += 1;\n    state.lastKey = \"j\";\n  });\n  return state;\n}\n```\n\nregisterAction throws if the key is not length 1, or if the binding exists and overwrite is false. Headless: harness.key('j') then step."
        },
        {
          "id": "failure",
          "anchor": "failure",
          "heading": "Failure and fallback",
          "kind": "normative",
          "required": true,
          "purpose": "How the feature fails closed, what events/diagnostics look like, and what the host should do.",
          "text": "registerAction throws on multi-character keys and on duplicate bindings without overwrite. Destroy must run on teardown or listeners leak across remounts; inject with an unbound key is a no-op."
        },
        {
          "id": "accessibility",
          "anchor": "accessibility",
          "heading": "Accessibility",
          "kind": "normative",
          "required": true,
          "purpose": "Keyboard, focus, reduced motion, captions/non-audio alternatives, contrast, and anything the feature itself publishes (e.g. semantic regions).",
          "text": "Prefer host-visible controls for essential actions; captured keys are easy to miss. Document the initiating key on the page. This manager has no focus management — keep page shortcuts when captureKeyboard is false."
        },
        {
          "id": "example",
          "anchor": "example",
          "heading": "Runnable example",
          "kind": "example",
          "required": true,
          "purpose": "Link canonical example IDs from the manifest. Embed and exact source come from the lock; this section must not paste a drifting snippet.",
          "text": "The embed below is the pinned keyboard-inject cart; it captures j after Run. Source lives on the example page, not here."
        },
        {
          "id": "expected-result",
          "anchor": "expected-result",
          "heading": "Expected result",
          "kind": "example",
          "required": true,
          "purpose": "What a reader should see, hear, or be able to do after the example runs.",
          "text": "After Run, press j. A bar grows with hits and the fill marks lastKey. Headless inject traces the same state without a window event."
        },
        {
          "id": "source",
          "anchor": "source",
          "heading": "Source, tests, and version",
          "kind": "provenance",
          "required": true,
          "purpose": "Links to implementationPath, tests, engine guide, and the pinned enginePackageVersion.",
          "text": "Implementation packages/engine/src/keyboardManager.ts. Tests: packages/engine/src/canvas/cyberArtRuntime.spec.ts. README section Write a cart."
        },
        {
          "id": "related",
          "anchor": "related",
          "heading": "Related features",
          "kind": "tutorial",
          "required": true,
          "purpose": "Prerequisites first, then relatedFeatureIds. Use manifest ids, not free text names.",
          "text": "Related: pointer, midi, runtime, cart-state. Prerequisites: none."
        },
        {
          "id": "advanced",
          "anchor": "advanced",
          "heading": "Advanced composition",
          "kind": "tutorial",
          "required": false,
          "purpose": "Optional: combining this feature with runtime groups, compositor, bindings, or scenarios.",
          "text": ""
        },
        {
          "id": "version",
          "anchor": "version",
          "heading": "Version provenance",
          "kind": "provenance",
          "required": true,
          "purpose": "Engine package version this page describes, when the API first appeared, and a warning if the live package.json has moved on.",
          "text": "Ships in @cyberart-io/engine 0.0.9. First appeared in 0.0.6."
        }
      ]
    },
    {
      "id": "pointer",
      "slug": "pointer",
      "route": "/docs/features/pointer",
      "title": "Pointer manager",
      "purpose": "Canvas pointer position, buttons, and click queue, with listen:false for deterministic inject.",
      "status": "substantial",
      "navGroup": "input",
      "packageName": "@cyberart-io/engine",
      "enginePackageVersion": "0.0.12",
      "compatibleEngineVersions": {
        "min": "0.0.6"
      },
      "documentationSource": {
        "kind": "site-page",
        "path": "src/docs/pages/pointer.ts"
      },
      "exampleIds": [],
      "relatedFeatureIds": [
        "keyboard",
        "normalized-geometry",
        "semantic-layers"
      ],
      "prerequisiteFeatureIds": [],
      "aliases": [],
      "keywords": [
        "pointer",
        "PointerManager"
      ],
      "surfaces": [
        "browser"
      ],
      "primaryExports": [
        "PointerManager"
      ],
      "agentRoute": "/docs/agent/features/pointer.json",
      "summary": "Canvas pointer position, buttons, and click queue, with listen:false for deterministic inject.",
      "owningModule": "./pointerManager",
      "lifecycle": {
        "status": "substantial",
        "introducedIn": "0.0.6",
        "compatibleEngineVersions": {
          "min": "0.0.6"
        },
        "normative": true,
        "experimental": false,
        "deprecated": false,
        "successorFeatureIds": []
      },
      "evidence": {
        "implementationPath": "packages/engine/src/pointerManager.ts",
        "tests": [
          "packages/engine/src/canvas/cyberArtRuntime.spec.ts"
        ]
      },
      "redirectsFrom": [],
      "examples": [],
      "pageSections": [
        {
          "id": "problem",
          "anchor": "problem",
          "heading": "What this solves",
          "kind": "tutorial",
          "required": true,
          "purpose": "Name the problem the feature exists to solve.",
          "text": "Carts need canvas pointer position, buttons, and a click queue, including a listen:false mode so tests can inject without a real pointer."
        },
        {
          "id": "when-to-use",
          "anchor": "when-to-use",
          "heading": "When to use it",
          "kind": "tutorial",
          "required": true,
          "purpose": "Say when this is the right tool, and when a related feature is a better fit.",
          "text": "Read PointerManager from update/render. Use keyboard for keys and midi for notes; normalized-geometry hit regions belong with that feature, not raw pixels here."
        },
        {
          "id": "conceptual-model",
          "anchor": "conceptual-model",
          "heading": "Conceptual model",
          "kind": "tutorial",
          "required": true,
          "purpose": "Objects, states, and how they relate. Diagrams optional; names must match the API.",
          "text": "PointerManager tracks position and buttons on the canvas. listen:false disables window/canvas listeners so inject(kind, x, y) is the only input; the click queue is consumed by the cart."
        },
        {
          "id": "lifecycle",
          "anchor": "lifecycle",
          "heading": "Lifecycle",
          "kind": "normative",
          "required": true,
          "purpose": "Create / attach / run / snapshot / restore / destroy, including which events fire.",
          "text": "Runtime constructs PointerManager at prepare. inject for tests; destroy with the animation so listeners do not leak."
        },
        {
          "id": "cart-vs-host",
          "anchor": "cart-vs-host",
          "heading": "Cart versus host",
          "kind": "normative",
          "required": true,
          "purpose": "What the cart may declare or emit versus what the host/runtime must provide. Do not blur the boundary.",
          "text": "The host owns the canvas element. The cart reads pointer state. Deterministic hosts inject; they do not require a real mouse."
        },
        {
          "id": "contract",
          "anchor": "contract",
          "heading": "Minimal contract",
          "kind": "normative",
          "required": true,
          "purpose": "Smallest API/configuration surface that a correct integration uses, with types and defaults.",
          "text": "```ts\npointer.inject('down', 10, 20);\npointer.inject('move', 12, 22);\npointer.inject('up', 12, 22);\n```\n\nPrimary export: PointerManager."
        },
        {
          "id": "failure",
          "anchor": "failure",
          "heading": "Failure and fallback",
          "kind": "normative",
          "required": true,
          "purpose": "How the feature fails closed, what events/diagnostics look like, and what the host should do.",
          "text": "Coordinates are canvas pixels, not CSS. Inject with listen true still works; listen false is required for hermetic tests. Destroy must run on remount."
        },
        {
          "id": "accessibility",
          "anchor": "accessibility",
          "heading": "Accessibility",
          "kind": "normative",
          "required": true,
          "purpose": "Keyboard, focus, reduced motion, captions/non-audio alternatives, contrast, and anything the feature itself publishes (e.g. semantic regions).",
          "text": "Pointer-only actions need a keyboard or host control equivalent. Pair with semantic-layers for named hit regions and a11y names."
        },
        {
          "id": "example",
          "anchor": "example",
          "heading": "Runnable example",
          "kind": "example",
          "required": true,
          "purpose": "Link canonical example IDs from the manifest. Embed and exact source come from the lock; this section must not paste a drifting snippet.",
          "text": "No canonical example yet. packages/engine/README.md (Write a cart) and packages/engine/src/canvas/cyberArtRuntime.spec.ts cover inject."
        },
        {
          "id": "expected-result",
          "anchor": "expected-result",
          "heading": "Expected result",
          "kind": "example",
          "required": true,
          "purpose": "What a reader should see, hear, or be able to do after the example runs.",
          "text": "After inject down/move/up, cart logic that reads the pointer sees those canvas coordinates and the click queue."
        },
        {
          "id": "source",
          "anchor": "source",
          "heading": "Source, tests, and version",
          "kind": "provenance",
          "required": true,
          "purpose": "Links to implementationPath, tests, engine guide, and the pinned enginePackageVersion.",
          "text": "Implementation packages/engine/src/pointerManager.ts. Tests: packages/engine/src/canvas/cyberArtRuntime.spec.ts."
        },
        {
          "id": "related",
          "anchor": "related",
          "heading": "Related features",
          "kind": "tutorial",
          "required": true,
          "purpose": "Prerequisites first, then relatedFeatureIds. Use manifest ids, not free text names.",
          "text": "Related: keyboard, normalized-geometry, semantic-layers. Prerequisites: none."
        },
        {
          "id": "advanced",
          "anchor": "advanced",
          "heading": "Advanced composition",
          "kind": "tutorial",
          "required": false,
          "purpose": "Optional: combining this feature with runtime groups, compositor, bindings, or scenarios.",
          "text": ""
        },
        {
          "id": "version",
          "anchor": "version",
          "heading": "Version provenance",
          "kind": "provenance",
          "required": true,
          "purpose": "Engine package version this page describes, when the API first appeared, and a warning if the live package.json has moved on.",
          "text": "Ships in @cyberart-io/engine 0.0.9. First appeared in 0.0.6."
        }
      ]
    },
    {
      "id": "midi",
      "slug": "midi",
      "route": "/docs/features/midi",
      "title": "MIDI",
      "purpose": "Host-owned MidiManager: note/CC/pitch in and out, inject/fake port, structured requestAccess.",
      "status": "substantial",
      "navGroup": "input",
      "packageName": "@cyberart-io/engine",
      "enginePackageVersion": "0.0.12",
      "compatibleEngineVersions": {
        "min": "0.0.6"
      },
      "documentationSource": {
        "kind": "site-page",
        "path": "src/docs/pages/midi.ts"
      },
      "exampleIds": [],
      "relatedFeatureIds": [
        "keyboard",
        "capability-manifest",
        "audio"
      ],
      "prerequisiteFeatureIds": [],
      "aliases": [],
      "keywords": [
        "midi",
        "MidiManager",
        "parseMidiBytes",
        "encodeMidiMessage"
      ],
      "surfaces": [
        "browser"
      ],
      "primaryExports": [
        "MidiManager",
        "parseMidiBytes",
        "encodeMidiMessage"
      ],
      "agentRoute": "/docs/agent/features/midi.json",
      "summary": "Host-owned MidiManager: note/CC/pitch in and out, inject/fake port, structured requestAccess.",
      "owningModule": "./midiManager",
      "lifecycle": {
        "status": "substantial",
        "introducedIn": "0.0.6",
        "compatibleEngineVersions": {
          "min": "0.0.6"
        },
        "normative": true,
        "experimental": false,
        "deprecated": false,
        "successorFeatureIds": []
      },
      "evidence": {
        "implementationPath": "packages/engine/src/midiManager.ts",
        "tests": [
          "packages/engine/src/midiManager.spec.ts",
          "packages/engine/src/canvas/cyb-19-midi.repro.spec.ts"
        ],
        "engineGuide": "packages/engine/docs/midi.md"
      },
      "redirectsFrom": [],
      "examples": [],
      "pageSections": [
        {
          "id": "problem",
          "anchor": "problem",
          "heading": "What this solves",
          "kind": "tutorial",
          "required": true,
          "purpose": "Name the problem the feature exists to solve.",
          "text": "Hosts need note/CC/pitch in and out without requiring Web MIDI hardware in tests, and without crashing silent carts when the API is missing."
        },
        {
          "id": "when-to-use",
          "anchor": "when-to-use",
          "heading": "When to use it",
          "kind": "tutorial",
          "required": true,
          "purpose": "Say when this is the right tool, and when a related feature is a better fit.",
          "text": "Construct MidiManager in the host or cart. Use keyboard for character keys. requestAccess wraps navigator.requestMIDIAccess; inject/fake port cover CI."
        },
        {
          "id": "conceptual-model",
          "anchor": "conceptual-model",
          "heading": "Conceptual model",
          "kind": "tutorial",
          "required": true,
          "purpose": "Objects, states, and how they relate. Diagrams optional; names must match the API.",
          "text": "Host-owned MidiManager. parseMidiBytes / encodeMidiMessage for raw payloads; inject({ kind: \"noteon\" | \"noteoff\" | \"cc\" | \"pitch\" }) or raw bytes; send through an injectable port."
        },
        {
          "id": "lifecycle",
          "anchor": "lifecycle",
          "heading": "Lifecycle",
          "kind": "normative",
          "required": true,
          "purpose": "Create / attach / run / snapshot / restore / destroy, including which events fire.",
          "text": "Construct → subscribe → requestAccess() (structured result, not throw) → inject or real events → send → destroy listeners with the owner."
        },
        {
          "id": "cart-vs-host",
          "anchor": "cart-vs-host",
          "heading": "Cart versus host",
          "kind": "normative",
          "required": true,
          "purpose": "What the cart may declare or emit versus what the host/runtime must provide. Do not blur the boundary.",
          "text": "The runtime does not pass MidiManager into getDefaultState: carts and hosts construct it. Headless/jsdom has no Web MIDI; inject and send still work via a test port."
        },
        {
          "id": "contract",
          "anchor": "contract",
          "heading": "Minimal contract",
          "kind": "normative",
          "required": true,
          "purpose": "Smallest API/configuration surface that a correct integration uses, with types and defaults.",
          "text": "```ts\nimport { MidiManager } from '@cyberart-io/engine';\n\nconst midi = new MidiManager();\nmidi.inject({ kind: 'noteon', channel: 0, note: 60, velocity: 100 });\n```\n\nrequestAccess() returns a structured ok/error; missing API or denied permission must not stop a silent cart."
        },
        {
          "id": "failure",
          "anchor": "failure",
          "heading": "Failure and fallback",
          "kind": "normative",
          "required": true,
          "purpose": "How the feature fails closed, what events/diagnostics look like, and what the host should do.",
          "text": "Missing Web MIDI is not an exception. Denied permission is a structured result. Capability manifests list midi as an integration — validate before assuming ports exist."
        },
        {
          "id": "accessibility",
          "anchor": "accessibility",
          "heading": "Accessibility",
          "kind": "normative",
          "required": true,
          "purpose": "Keyboard, focus, reduced motion, captions/non-audio alternatives, contrast, and anything the feature itself publishes (e.g. semantic regions).",
          "text": "MIDI is not a substitute for keyboard. Provide a non-MIDI control path, and do not require hardware for docs or CI."
        },
        {
          "id": "example",
          "anchor": "example",
          "heading": "Runnable example",
          "kind": "example",
          "required": true,
          "purpose": "Link canonical example IDs from the manifest. Embed and exact source come from the lock; this section must not paste a drifting snippet.",
          "text": "No canonical example yet. packages/engine/docs/midi.md and packages/engine/src/canvas/cyb-19-midi.repro.spec.ts are the runtime proof."
        },
        {
          "id": "expected-result",
          "anchor": "expected-result",
          "heading": "Expected result",
          "kind": "example",
          "required": true,
          "purpose": "What a reader should see, hear, or be able to do after the example runs.",
          "text": "inject noteon/noteoff/cc/pitch notifies subscribers. requestAccess without Web MIDI returns a structured failure and the cart keeps running."
        },
        {
          "id": "source",
          "anchor": "source",
          "heading": "Source, tests, and version",
          "kind": "provenance",
          "required": true,
          "purpose": "Links to implementationPath, tests, engine guide, and the pinned enginePackageVersion.",
          "text": "Implementation packages/engine/src/midiManager.ts. Tests: packages/engine/src/midiManager.spec.ts and packages/engine/src/canvas/cyb-19-midi.repro.spec.ts. Guide: packages/engine/docs/midi.md."
        },
        {
          "id": "related",
          "anchor": "related",
          "heading": "Related features",
          "kind": "tutorial",
          "required": true,
          "purpose": "Prerequisites first, then relatedFeatureIds. Use manifest ids, not free text names.",
          "text": "Related: keyboard, capability-manifest, audio. Prerequisites: none."
        },
        {
          "id": "advanced",
          "anchor": "advanced",
          "heading": "Advanced composition",
          "kind": "tutorial",
          "required": false,
          "purpose": "Optional: combining this feature with runtime groups, compositor, bindings, or scenarios.",
          "text": ""
        },
        {
          "id": "version",
          "anchor": "version",
          "heading": "Version provenance",
          "kind": "provenance",
          "required": true,
          "purpose": "Engine package version this page describes, when the API first appeared, and a warning if the live package.json has moved on.",
          "text": "Ships in @cyberart-io/engine 0.0.9. First appeared in 0.0.6."
        }
      ]
    },
    {
      "id": "calculation-carts",
      "slug": "calculation-carts",
      "route": "/docs/features/calculation-carts",
      "title": "Calculation carts",
      "purpose": "kind: calculation runs update and events without a canvas so a sibling render cart can paint.",
      "status": "substantial",
      "navGroup": "core",
      "packageName": "@cyberart-io/engine",
      "enginePackageVersion": "0.0.12",
      "compatibleEngineVersions": {
        "min": "0.0.7"
      },
      "documentationSource": {
        "kind": "site-page",
        "path": "src/docs/pages/calculation-carts.ts"
      },
      "exampleIds": [
        "calculation-capability"
      ],
      "relatedFeatureIds": [
        "runtime",
        "runtime-group"
      ],
      "prerequisiteFeatureIds": [
        "runtime"
      ],
      "aliases": [],
      "keywords": [
        "calculation",
        "CartKind",
        "kind: calculation",
        "no-canvas"
      ],
      "surfaces": [
        "browser"
      ],
      "primaryExports": [
        "CartKind"
      ],
      "agentRoute": "/docs/agent/features/calculation-carts.json",
      "summary": "kind: calculation runs update and events without a canvas so a sibling render cart can paint.",
      "owningModule": "./canvas/animation",
      "lifecycle": {
        "status": "substantial",
        "introducedIn": "0.0.7",
        "compatibleEngineVersions": {
          "min": "0.0.7"
        },
        "normative": true,
        "experimental": false,
        "deprecated": false,
        "successorFeatureIds": []
      },
      "evidence": {
        "implementationPath": "packages/engine/src/canvas/animation.ts",
        "tests": [
          "packages/engine/src/canvas/cyb-20-calculation-cart.repro.spec.ts",
          "packages/engine/src/canvas/runtimeGroup.spec.ts"
        ],
        "engineGuide": "packages/engine/docs/calculation-carts.md"
      },
      "redirectsFrom": [],
      "examples": [
        {
          "id": "calculation-capability",
          "route": "/docs/examples/calculation-capability",
          "title": "Calculation cart with capability manifest",
          "purpose": "Show a calculation cart that updates from host-channel events without a canvas, plus a versioned engine capability manifest the host can validate. Not a game.",
          "featureIds": [
            "calculation-carts",
            "capability-manifest",
            "headless-harness"
          ],
          "runtimeKind": "calculation",
          "artifact": {
            "state": "available",
            "reason": "Cache verified against the lock."
          },
          "source": {
            "state": "restricted",
            "reason": "Source not published; example-carts is private.",
            "files": [
              "cart.ts",
              "main.ts",
              "capabilities.json",
              "manifest.json"
            ]
          },
          "provenance": {
            "repository": "cyberart-io/example-carts",
            "commit": "2ed0eb599ded827cca46bcd2fc66dea78f387a0c",
            "engineVersion": "0.0.12",
            "sourceUrl": null
          },
          "capabilities": {
            "permissions": [
              "host.intent.*",
              "host.state.*"
            ],
            "assets": [],
            "network": "none",
            "credentials": "none",
            "engineCapabilityFile": "capabilities.json"
          },
          "interaction": {
            "mode": "scripted",
            "notes": "The host dispatches host.state.scene-changed. There is no pointer or keyboard surface."
          },
          "expected": {
            "visible": "No canvas. After a scene-changed event, cart state.sceneId matches the payload and the cart emits host.intent.overlay with overlayId banner.",
            "audible": "silent"
          },
          "accessibility": {
            "notes": "No visual surface. Docs should describe the event exchange in text.",
            "fallback": "State and emitted events are the accessible representation."
          },
          "viewport": {
            "width": 320,
            "height": 180
          }
        }
      ],
      "pageSections": [
        {
          "id": "problem",
          "anchor": "problem",
          "heading": "What this solves",
          "kind": "tutorial",
          "required": true,
          "purpose": "Name the problem the feature exists to solve.",
          "text": "Some carts should run update and events without painting so a sibling render cart can own the canvas."
        },
        {
          "id": "when-to-use",
          "anchor": "when-to-use",
          "heading": "When to use it",
          "kind": "tutorial",
          "required": true,
          "purpose": "Say when this is the right tool, and when a related feature is a better fit.",
          "text": "Pass kind: \"calculation\" to createRuntime (or a group participant) when the cart has no render; Use a render runtime for anything that paints. Do not omit render on a render runtime and expect a canvas anyway."
        },
        {
          "id": "conceptual-model",
          "anchor": "conceptual-model",
          "heading": "Conceptual model",
          "kind": "tutorial",
          "required": true,
          "purpose": "Objects, states, and how they relate. Diagrams optional; names must match the API.",
          "text": "CartKind is \"render\" | \"calculation\"; A calculation cart is the same AnimationCart state machine without a canvas: update + host-channel events, no render call. Sibling render carts still paint; Headless createRuntime({ kind: \"calculation\", deterministic }) is the CI path."
        },
        {
          "id": "lifecycle",
          "anchor": "lifecycle",
          "heading": "Lifecycle",
          "kind": "normative",
          "required": true,
          "purpose": "Create / attach / run / snapshot / restore / destroy, including which events fire.",
          "text": "createRuntime({ container, kind: \"calculation\" }) → mount → start or step → consume/emit on the host channel → destroy. mounted.canvas is undefined; the container has no canvas element."
        },
        {
          "id": "cart-vs-host",
          "anchor": "cart-vs-host",
          "heading": "Cart versus host",
          "kind": "normative",
          "required": true,
          "purpose": "What the cart may declare or emit versus what the host/runtime must provide. Do not blur the boundary.",
          "text": "The host sets kind and owns the channel; the cart updates state from consume() and emit()s intents. Capability manifests declare emit/subscribe patterns so the host can validate before mount."
        },
        {
          "id": "contract",
          "anchor": "contract",
          "heading": "Minimal contract",
          "kind": "normative",
          "required": true,
          "purpose": "Smallest API/configuration surface that a correct integration uses, with types and defaults.",
          "text": "```ts\nconst calcRuntime = createRuntime({\n  container: stage,\n  kind: \"calculation\",\n  deterministic: { origin: 0 },\n});\nconst handle = calcRuntime.mount(world);\nhandle.dispatch({ type: \"host.state.scene-changed\", payload: { sceneId: \"alpha\" } });\nawait handle.step(1);\n```\n\nPrimary export: CartKind. createRuntime stays on the runtime feature."
        },
        {
          "id": "failure",
          "anchor": "failure",
          "heading": "Failure and fallback",
          "kind": "normative",
          "required": true,
          "purpose": "How the feature fails closed, what events/diagnostics look like, and what the host should do.",
          "text": "A calculation runtime must not create a canvas; hosts that need paint mount a second render cart. Capability validation fails closed when emit/subscribe do not match the host."
        },
        {
          "id": "accessibility",
          "anchor": "accessibility",
          "heading": "Accessibility",
          "kind": "normative",
          "required": true,
          "purpose": "Keyboard, focus, reduced motion, captions/non-audio alternatives, contrast, and anything the feature itself publishes (e.g. semantic regions).",
          "text": "There is no visual surface; docs should describe the event exchange in text. State and emitted events are the accessible representation."
        },
        {
          "id": "example",
          "anchor": "example",
          "heading": "Runnable example",
          "kind": "example",
          "required": true,
          "purpose": "Link canonical example IDs from the manifest. Embed and exact source come from the lock; this section must not paste a drifting snippet.",
          "text": "Canonical example calculation-capability is bound to calculation-carts, capability-manifest, and headless-harness. This page does not copy cart.ts."
        },
        {
          "id": "expected-result",
          "anchor": "expected-result",
          "heading": "Expected result",
          "kind": "example",
          "required": true,
          "purpose": "What a reader should see, hear, or be able to do after the example runs.",
          "text": "No canvas. After scene-changed, state.sceneId is alpha and host.intent.overlay fires with overlayId banner."
        },
        {
          "id": "source",
          "anchor": "source",
          "heading": "Source, tests, and version",
          "kind": "provenance",
          "required": true,
          "purpose": "Links to implementationPath, tests, engine guide, and the pinned enginePackageVersion.",
          "text": "Implementation packages/engine/src/canvas/animation.ts (CartKind). Tests: packages/engine/src/canvas/cyb-20-calculation-cart.repro.spec.ts and packages/engine/src/canvas/runtimeGroup.spec.ts. Guide: packages/engine/docs/calculation-carts.md."
        },
        {
          "id": "related",
          "anchor": "related",
          "heading": "Related features",
          "kind": "tutorial",
          "required": true,
          "purpose": "Prerequisites first, then relatedFeatureIds. Use manifest ids, not free text names.",
          "text": "Prerequisite: runtime. Related: runtime-group."
        },
        {
          "id": "advanced",
          "anchor": "advanced",
          "heading": "Advanced composition",
          "kind": "tutorial",
          "required": false,
          "purpose": "Optional: combining this feature with runtime groups, compositor, bindings, or scenarios.",
          "text": "createRuntimeGroup can mix calculation and render participants on one clock and router."
        },
        {
          "id": "version",
          "anchor": "version",
          "heading": "Version provenance",
          "kind": "provenance",
          "required": true,
          "purpose": "Engine package version this page describes, when the API first appeared, and a warning if the live package.json has moved on.",
          "text": "Ships in @cyberart-io/engine 0.0.9. First appeared in 0.0.7."
        }
      ]
    },
    {
      "id": "deterministic-mode",
      "slug": "deterministic-mode",
      "route": "/docs/features/deterministic-mode",
      "title": "Deterministic mode",
      "purpose": "step/schedule, virtual clocks, ScriptedAction, and replay diffs so CI can drive a cart without wall time.",
      "status": "substantial",
      "navGroup": "core",
      "packageName": "@cyberart-io/engine",
      "enginePackageVersion": "0.0.12",
      "compatibleEngineVersions": {
        "min": "0.0.6"
      },
      "documentationSource": {
        "kind": "site-page",
        "path": "src/docs/pages/deterministic-mode.ts"
      },
      "exampleIds": [],
      "relatedFeatureIds": [
        "runtime",
        "events",
        "headless-harness",
        "replay-inspector"
      ],
      "prerequisiteFeatureIds": [
        "runtime"
      ],
      "aliases": [],
      "keywords": [
        "deterministic-mode",
        "createVirtualClock",
        "createWallClock",
        "describeReplayMismatch"
      ],
      "surfaces": [
        "browser"
      ],
      "primaryExports": [
        "createVirtualClock",
        "createWallClock",
        "describeReplayMismatch"
      ],
      "agentRoute": "/docs/agent/features/deterministic-mode.json",
      "summary": "step/schedule, virtual clocks, ScriptedAction, and replay diffs so CI can drive a cart without wall time.",
      "owningModule": "./canvas/cyberArtRuntime",
      "lifecycle": {
        "status": "substantial",
        "introducedIn": "0.0.6",
        "compatibleEngineVersions": {
          "min": "0.0.6"
        },
        "normative": true,
        "experimental": false,
        "deprecated": false,
        "successorFeatureIds": []
      },
      "evidence": {
        "implementationPath": "packages/engine/src/canvas/deterministicMode.ts",
        "tests": [
          "packages/engine/src/canvas/deterministicMode.spec.ts",
          "packages/engine/src/canvas/cyberArtRuntime.spec.ts"
        ],
        "engineGuide": "packages/engine/docs/deterministic-mode.md"
      },
      "redirectsFrom": [],
      "examples": [],
      "pageSections": [
        {
          "id": "problem",
          "anchor": "problem",
          "heading": "What this solves",
          "kind": "tutorial",
          "required": true,
          "purpose": "Name the problem the feature exists to solve.",
          "text": "Seeded Random is not enough to reproduce an interactive scene. CI needs to own time and input."
        },
        {
          "id": "when-to-use",
          "anchor": "when-to-use",
          "heading": "When to use it",
          "kind": "tutorial",
          "required": true,
          "purpose": "Say when this is the right tool, and when a related feature is a better fit.",
          "text": "Pass deterministic: { origin } to createRuntime for tests and replay; leave it unset for kaleidoscope / Art Blocks. Headless harness always uses deterministic mode."
        },
        {
          "id": "conceptual-model",
          "anchor": "conceptual-model",
          "heading": "Conceptual model",
          "kind": "tutorial",
          "required": true,
          "purpose": "Objects, states, and how they relate. Diagrams optional; names must match the API.",
          "text": "step/schedule, virtual clocks, ScriptedAction, and replay diffs; createVirtualClock vs createWallClock. describeReplayMismatch explains a failed replay."
        },
        {
          "id": "lifecycle",
          "anchor": "lifecycle",
          "heading": "Lifecycle",
          "kind": "normative",
          "required": true,
          "purpose": "Create / attach / run / snapshot / restore / destroy, including which events fire.",
          "text": "createRuntime({ deterministic }) → mount → step(frames) / advance(ms) / schedule(action) → compare replay metadata. step throws if deterministic was not set."
        },
        {
          "id": "cart-vs-host",
          "anchor": "cart-vs-host",
          "heading": "Cart versus host",
          "kind": "normative",
          "required": true,
          "purpose": "What the cart may declare or emit versus what the host/runtime must provide. Do not blur the boundary.",
          "text": "The host owns the clock and scripted actions; the cart remains a pure state machine of those inputs. Do not read Date.now() inside cart update if you need replay."
        },
        {
          "id": "contract",
          "anchor": "contract",
          "heading": "Minimal contract",
          "kind": "normative",
          "required": true,
          "purpose": "Smallest API/configuration surface that a correct integration uses, with types and defaults.",
          "text": "```ts\nconst hostRuntime = createRuntime({\n  container: stage,\n  deterministic: { origin: 0 },\n  seed,\n});\nconst handle = hostRuntime.mount(cart);\nawait handle.step(3);\n```\n\nPrimary exports: createVirtualClock, createWallClock, describeReplayMismatch."
        },
        {
          "id": "failure",
          "anchor": "failure",
          "heading": "Failure and fallback",
          "kind": "normative",
          "required": true,
          "purpose": "How the feature fails closed, what events/diagnostics look like, and what the host should do.",
          "text": "step/advance/schedule throw when deterministic was not set; replay mismatches are data (describeReplayMismatch), not silent pass. Wall-clock mode is not a CI oracle."
        },
        {
          "id": "accessibility",
          "anchor": "accessibility",
          "heading": "Accessibility",
          "kind": "normative",
          "required": true,
          "purpose": "Keyboard, focus, reduced motion, captions/non-audio alternatives, contrast, and anything the feature itself publishes (e.g. semantic regions).",
          "text": "Deterministic mode is a host/testing contract. User-facing a11y does not change; scripted keys still need documented initiating actions."
        },
        {
          "id": "example",
          "anchor": "example",
          "heading": "Runnable example",
          "kind": "example",
          "required": true,
          "purpose": "Link canonical example IDs from the manifest. Embed and exact source come from the lock; this section must not paste a drifting snippet.",
          "text": "No canonical example yet. packages/engine/docs/deterministic-mode.md and packages/engine/src/canvas/deterministicMode.spec.ts are the runtime proof."
        },
        {
          "id": "expected-result",
          "anchor": "expected-result",
          "heading": "Expected result",
          "kind": "example",
          "required": true,
          "purpose": "What a reader should see, hear, or be able to do after the example runs.",
          "text": "step(3) advances framesElapsed by 3 with origin 0, independent of wall time."
        },
        {
          "id": "source",
          "anchor": "source",
          "heading": "Source, tests, and version",
          "kind": "provenance",
          "required": true,
          "purpose": "Links to implementationPath, tests, engine guide, and the pinned enginePackageVersion.",
          "text": "Implementation packages/engine/src/canvas/deterministicMode.ts. Tests: packages/engine/src/canvas/deterministicMode.spec.ts and packages/engine/src/canvas/cyberArtRuntime.spec.ts. Guide: packages/engine/docs/deterministic-mode.md."
        },
        {
          "id": "related",
          "anchor": "related",
          "heading": "Related features",
          "kind": "tutorial",
          "required": true,
          "purpose": "Prerequisites first, then relatedFeatureIds. Use manifest ids, not free text names.",
          "text": "Prerequisite: runtime. Related: events, headless-harness, replay-inspector."
        },
        {
          "id": "advanced",
          "anchor": "advanced",
          "heading": "Advanced composition",
          "kind": "tutorial",
          "required": false,
          "purpose": "Optional: combining this feature with runtime groups, compositor, bindings, or scenarios.",
          "text": ""
        },
        {
          "id": "version",
          "anchor": "version",
          "heading": "Version provenance",
          "kind": "provenance",
          "required": true,
          "purpose": "Engine package version this page describes, when the API first appeared, and a warning if the live package.json has moved on.",
          "text": "Ships in @cyberart-io/engine 0.0.9. First appeared in 0.0.6."
        }
      ]
    },
    {
      "id": "events",
      "slug": "events",
      "route": "/docs/features/events",
      "title": "Events and router",
      "purpose": "Mailbox envelope, typed contracts, createEventRouter, hops, idempotency, and rejections.",
      "status": "substantial",
      "navGroup": "events-presentation",
      "packageName": "@cyberart-io/engine",
      "enginePackageVersion": "0.0.12",
      "compatibleEngineVersions": {
        "min": "0.0.6"
      },
      "documentationSource": {
        "kind": "site-page",
        "path": "src/docs/pages/events.ts"
      },
      "exampleIds": [],
      "relatedFeatureIds": [
        "runtime",
        "presentation-adapter",
        "capability-manifest",
        "replay-inspector"
      ],
      "prerequisiteFeatureIds": [],
      "aliases": [],
      "keywords": [
        "events",
        "createEventRouter",
        "normalizeEvent",
        "createContractRegistry"
      ],
      "surfaces": [
        "browser"
      ],
      "primaryExports": [
        "createEventRouter",
        "normalizeEvent",
        "createContractRegistry"
      ],
      "agentRoute": "/docs/agent/features/events.json",
      "summary": "Mailbox envelope, typed contracts, createEventRouter, hops, idempotency, and rejections.",
      "owningModule": "./canvas/eventRouter",
      "lifecycle": {
        "status": "substantial",
        "introducedIn": "0.0.6",
        "compatibleEngineVersions": {
          "min": "0.0.6"
        },
        "normative": true,
        "experimental": false,
        "deprecated": false,
        "successorFeatureIds": []
      },
      "evidence": {
        "implementationPath": "packages/engine/src/canvas/eventRouter.ts",
        "tests": [
          "packages/engine/src/canvas/eventRouter.spec.ts",
          "packages/engine/src/canvas/eventEnvelope.spec.ts",
          "packages/engine/src/canvas/eventContract.spec.ts",
          "packages/engine/src/canvas/cyb-59-event-contract.repro.spec.ts"
        ],
        "engineGuide": "packages/engine/docs/events.md"
      },
      "redirectsFrom": [],
      "examples": [],
      "pageSections": [
        {
          "id": "problem",
          "anchor": "problem",
          "heading": "What this solves",
          "kind": "tutorial",
          "required": true,
          "purpose": "Name the problem the feature exists to solve.",
          "text": "Carts and hosts need a mailbox envelope, typed contracts, routing, hops, idempotency, and explicit rejections — not ad-hoc window events."
        },
        {
          "id": "when-to-use",
          "anchor": "when-to-use",
          "heading": "When to use it",
          "kind": "tutorial",
          "required": true,
          "purpose": "Say when this is the right tool, and when a related feature is a better fit.",
          "text": "createEventRouter / normalizeEvent / createContractRegistry for host↔cart messages; presentation-adapter consumes intents. Capability-manifest declares emit/subscribe; Do not use this as a DOM event bus for the docs shell."
        },
        {
          "id": "conceptual-model",
          "anchor": "conceptual-model",
          "heading": "Conceptual model",
          "kind": "tutorial",
          "required": true,
          "purpose": "Objects, states, and how they relate. Diagrams optional; names must match the API.",
          "text": "Envelope + typed contracts; createEventRouter routes; hops bound forwarding; idempotency keys drop duplicates; rejections are structured. HostChannel consume/emit is the cart-facing mailbox."
        },
        {
          "id": "lifecycle",
          "anchor": "lifecycle",
          "heading": "Lifecycle",
          "kind": "normative",
          "required": true,
          "purpose": "Create / attach / run / snapshot / restore / destroy, including which events fire.",
          "text": "normalizeEvent → router attach → emit/consume each frame → reject or ack. Destroy detaches the mailbox with the runtime."
        },
        {
          "id": "cart-vs-host",
          "anchor": "cart-vs-host",
          "heading": "Cart versus host",
          "kind": "normative",
          "required": true,
          "purpose": "What the cart may declare or emit versus what the host/runtime must provide. Do not blur the boundary.",
          "text": "The host owns the router and which contracts exist; the cart consume()s inbound and emit()s outbound. Unknown types fail closed when a contract registry is installed."
        },
        {
          "id": "contract",
          "anchor": "contract",
          "heading": "Minimal contract",
          "kind": "normative",
          "required": true,
          "purpose": "Smallest API/configuration surface that a correct integration uses, with types and defaults.",
          "text": "```ts\nimport { createEventRouter, normalizeEvent } from '@cyberart-io/engine';\n\nconst router = createEventRouter();\nhandle.dispatch({ type: 'host.state.scene-changed', payload: { sceneId: 'alpha' } });\n```\n\nPrimary exports: createEventRouter, normalizeEvent, createContractRegistry."
        },
        {
          "id": "failure",
          "anchor": "failure",
          "heading": "Failure and fallback",
          "kind": "normative",
          "required": true,
          "purpose": "How the feature fails closed, what events/diagnostics look like, and what the host should do.",
          "text": "Unknown contracts, hop exhaustion, and duplicate idempotency keys are rejections — not thrown UI exceptions. cyb-59-event-contract.repro.spec.ts pins the contract."
        },
        {
          "id": "accessibility",
          "anchor": "accessibility",
          "heading": "Accessibility",
          "kind": "normative",
          "required": true,
          "purpose": "Keyboard, focus, reduced motion, captions/non-audio alternatives, contrast, and anything the feature itself publishes (e.g. semantic regions).",
          "text": "Events are not a visual surface; surface important host intents in text or semantic layers. Do not rely on a canvas-only toast."
        },
        {
          "id": "example",
          "anchor": "example",
          "heading": "Runnable example",
          "kind": "example",
          "required": true,
          "purpose": "Link canonical example IDs from the manifest. Embed and exact source come from the lock; this section must not paste a drifting snippet.",
          "text": "No canonical example yet; packages/engine/docs/events.md and packages/engine/src/canvas/cyb-59-event-contract.repro.spec.ts are the runtime proof. calculation-capability shows a host-channel exchange."
        },
        {
          "id": "expected-result",
          "anchor": "expected-result",
          "heading": "Expected result",
          "kind": "example",
          "required": true,
          "purpose": "What a reader should see, hear, or be able to do after the example runs.",
          "text": "A dispatched host.state.scene-changed is consumed by the cart on the next step. Duplicate idempotent envelopes do not double-apply."
        },
        {
          "id": "source",
          "anchor": "source",
          "heading": "Source, tests, and version",
          "kind": "provenance",
          "required": true,
          "purpose": "Links to implementationPath, tests, engine guide, and the pinned enginePackageVersion.",
          "text": "Implementation packages/engine/src/canvas/eventRouter.ts. Tests: eventRouter.spec.ts, eventEnvelope.spec.ts, eventContract.spec.ts, cyb-59-event-contract.repro.spec.ts. Guide: packages/engine/docs/events.md."
        },
        {
          "id": "related",
          "anchor": "related",
          "heading": "Related features",
          "kind": "tutorial",
          "required": true,
          "purpose": "Prerequisites first, then relatedFeatureIds. Use manifest ids, not free text names.",
          "text": "Related: runtime, presentation-adapter, capability-manifest, replay-inspector. Prerequisites: none."
        },
        {
          "id": "advanced",
          "anchor": "advanced",
          "heading": "Advanced composition",
          "kind": "tutorial",
          "required": false,
          "purpose": "Optional: combining this feature with runtime groups, compositor, bindings, or scenarios.",
          "text": ""
        },
        {
          "id": "version",
          "anchor": "version",
          "heading": "Version provenance",
          "kind": "provenance",
          "required": true,
          "purpose": "Engine package version this page describes, when the API first appeared, and a warning if the live package.json has moved on.",
          "text": "Ships in @cyberart-io/engine 0.0.9. First appeared in 0.0.6."
        }
      ]
    },
    {
      "id": "snapshots",
      "slug": "snapshots",
      "route": "/docs/features/snapshots",
      "title": "Snapshots",
      "purpose": "Versioned snapshot envelope, defineSnapshot / parseSnapshot, and host-owned migrations.",
      "status": "substantial",
      "navGroup": "core",
      "packageName": "@cyberart-io/engine",
      "enginePackageVersion": "0.0.12",
      "compatibleEngineVersions": {
        "min": "0.0.6"
      },
      "documentationSource": {
        "kind": "site-page",
        "path": "src/docs/pages/snapshots.ts"
      },
      "exampleIds": [
        "snapshot-restore"
      ],
      "relatedFeatureIds": [
        "runtime",
        "cart-state",
        "replay-inspector"
      ],
      "prerequisiteFeatureIds": [],
      "aliases": [],
      "keywords": [
        "snapshots",
        "defineSnapshot",
        "parseSnapshot",
        "applySnapshotMigrations"
      ],
      "surfaces": [
        "browser"
      ],
      "primaryExports": [
        "defineSnapshot",
        "parseSnapshot",
        "applySnapshotMigrations"
      ],
      "agentRoute": "/docs/agent/features/snapshots.json",
      "summary": "Versioned snapshot envelope, defineSnapshot / parseSnapshot, and host-owned migrations.",
      "owningModule": "./canvas/snapshotEnvelope",
      "lifecycle": {
        "status": "substantial",
        "introducedIn": "0.0.6",
        "compatibleEngineVersions": {
          "min": "0.0.6"
        },
        "normative": true,
        "experimental": false,
        "deprecated": false,
        "successorFeatureIds": []
      },
      "evidence": {
        "implementationPath": "packages/engine/src/canvas/snapshotEnvelope.ts",
        "tests": [
          "packages/engine/src/canvas/snapshotEnvelope.spec.ts",
          "packages/engine/src/canvas/cyb-50-snapshots.repro.spec.ts"
        ],
        "engineGuide": "packages/engine/docs/snapshots.md"
      },
      "redirectsFrom": [],
      "examples": [
        {
          "id": "snapshot-restore",
          "route": "/docs/examples/snapshot-restore",
          "title": "Snapshot export and restore",
          "purpose": "Show versioned snapshots: ticks grow a strip; exportSnapshot then importSnapshot restores the saved width. Reset remounts at zero. No audio, network, or credentials.",
          "featureIds": [
            "snapshots"
          ],
          "runtimeKind": "render",
          "artifact": {
            "state": "available",
            "reason": "Cache verified against the lock."
          },
          "source": {
            "state": "restricted",
            "reason": "Source not published; example-carts is private.",
            "files": [
              "cart.ts",
              "main.ts",
              "manifest.json"
            ]
          },
          "provenance": {
            "repository": "cyberart-io/example-carts",
            "commit": "2ed0eb599ded827cca46bcd2fc66dea78f387a0c",
            "engineVersion": "0.0.12",
            "sourceUrl": null
          },
          "capabilities": {
            "permissions": [],
            "assets": [],
            "network": "none",
            "credentials": "none"
          },
          "interaction": {
            "mode": "none",
            "notes": "State changes every frame. Tests export/import. Docs Reset remounts."
          },
          "expected": {
            "visible": "An orange strip grows with ticks. After importSnapshot the strip jumps back to the saved width. Reset remounts at zero.",
            "audible": "silent"
          },
          "accessibility": {
            "notes": "Strip width is the tick count. Restore vs live should be obvious. Reset returns to the default state.",
            "fallback": "Text: ticks increment each frame; importSnapshot restores the saved tick count."
          },
          "viewport": {
            "width": 320,
            "height": 180
          }
        }
      ],
      "pageSections": [
        {
          "id": "problem",
          "anchor": "problem",
          "heading": "What this solves",
          "kind": "tutorial",
          "required": true,
          "purpose": "Name the problem the feature exists to solve.",
          "text": "Hosts need a portable save envelope that separates engine cart state from host-owned data, with migrations, without this package becoming the database of record."
        },
        {
          "id": "when-to-use",
          "anchor": "when-to-use",
          "heading": "When to use it",
          "kind": "tutorial",
          "required": true,
          "purpose": "Say when this is the right tool, and when a related feature is a better fit.",
          "text": "Use defineSnapshot / parseSnapshot / exportSnapshot / importSnapshot for versioned saves; attachCartStatePersistence (cart-state) is the localStorage helper, not a second schema. Do not treat getCartState() as JSON-safe."
        },
        {
          "id": "conceptual-model",
          "anchor": "conceptual-model",
          "heading": "Conceptual model",
          "kind": "tutorial",
          "required": true,
          "purpose": "Objects, states, and how they relate. Diagrams optional; names must match the API.",
          "text": "Schema 1 is the engine-owned CartStateBundle; schema 2 wraps that blob in engineState and keeps host-owned data in hostState / hostStateRef. cart.id is required; JSON round-trip equals the snapshot. importState still accepts the legacy blob and also a current envelope (migrates, restores engineState only)."
        },
        {
          "id": "lifecycle",
          "anchor": "lifecycle",
          "heading": "Lifecycle",
          "kind": "normative",
          "required": true,
          "purpose": "Create / attach / run / snapshot / restore / destroy, including which events fire.",
          "text": "exportSnapshot wraps exportState into a schema 2 envelope (clock, rng, optional hostState) → persist however the host wants → importSnapshot migrates to the current schema and restores engineState. Hosts reapply hostState; applySnapshotMigrations walks from → from+1 and leaves the original object unchanged on failure."
        },
        {
          "id": "cart-vs-host",
          "anchor": "cart-vs-host",
          "heading": "Cart versus host",
          "kind": "normative",
          "required": true,
          "purpose": "What the cart may declare or emit versus what the host/runtime must provide. Do not blur the boundary.",
          "text": "The engine validates and migrates envelopes in memory; the host owns persistence (localStorage, a database, cross-user saves), hostState, and provenance.integrity. Cart importState does not replay the PRNG."
        },
        {
          "id": "contract",
          "anchor": "contract",
          "heading": "Minimal contract",
          "kind": "normative",
          "required": true,
          "purpose": "Smallest API/configuration surface that a correct integration uses, with types and defaults.",
          "text": "```ts\nconst envelope = await handle.exportSnapshot({\n  cartVersion: '1',\n  hostState: { sceneId: 'alpha' },\n});\nawait handle.importSnapshot(envelope);\n```\n\ndefineSnapshot / parseSnapshot / validateSnapshot return { ok, snapshot } or { ok: false, errors }. They do not throw."
        },
        {
          "id": "failure",
          "anchor": "failure",
          "heading": "Failure and fallback",
          "kind": "normative",
          "required": true,
          "purpose": "How the feature fails closed, what events/diagnostics look like, and what the host should do.",
          "text": "Missing cart.id fails closed (no stamped \"unknown\"); parseSnapshot invalid-json / invalid-snapshot / invalid-field / invalid-schema / invalid-engine-state. applySnapshotMigrations fails closed when a step is missing; generative carts refuse to load onto a different hash."
        },
        {
          "id": "accessibility",
          "anchor": "accessibility",
          "heading": "Accessibility",
          "kind": "normative",
          "required": true,
          "purpose": "Keyboard, focus, reduced motion, captions/non-audio alternatives, contrast, and anything the feature itself publishes (e.g. semantic regions).",
          "text": "Make restored vs live state obvious visually or in text; Provide Reset. getCartState is for debug UI, not a save format; Do not hide restore failures — surface envelope errors to the host."
        },
        {
          "id": "example",
          "anchor": "example",
          "heading": "Runnable example",
          "kind": "example",
          "required": true,
          "purpose": "Link canonical example IDs from the manifest. Embed and exact source come from the lock; this section must not paste a drifting snippet.",
          "text": "Canonical example snapshot-restore is bound in the manifest; ticks change the strip; import restores the saved width. This page does not copy cart.ts."
        },
        {
          "id": "expected-result",
          "anchor": "expected-result",
          "heading": "Expected result",
          "kind": "example",
          "required": true,
          "purpose": "What a reader should see, hear, or be able to do after the example runs.",
          "text": "The strip grows with ticks; After importSnapshot the strip matches the saved tick count. Reset remounts at zero."
        },
        {
          "id": "source",
          "anchor": "source",
          "heading": "Source, tests, and version",
          "kind": "provenance",
          "required": true,
          "purpose": "Links to implementationPath, tests, engine guide, and the pinned enginePackageVersion.",
          "text": "Implementation packages/engine/src/canvas/snapshotEnvelope.ts. Tests: packages/engine/src/canvas/snapshotEnvelope.spec.ts and packages/engine/src/canvas/cyb-50-snapshots.repro.spec.ts. Guide: packages/engine/docs/snapshots.md."
        },
        {
          "id": "related",
          "anchor": "related",
          "heading": "Related features",
          "kind": "tutorial",
          "required": true,
          "purpose": "Prerequisites first, then relatedFeatureIds. Use manifest ids, not free text names.",
          "text": "Related: runtime, cart-state, replay-inspector. Prerequisites: none."
        },
        {
          "id": "advanced",
          "anchor": "advanced",
          "heading": "Advanced composition",
          "kind": "tutorial",
          "required": false,
          "purpose": "Optional: combining this feature with runtime groups, compositor, bindings, or scenarios.",
          "text": "Modules that persist their own snapshot() JSON inside hostState include semantic-layers, presentation-bindings, portal-lifecycle, job-orchestration, world-graph, world-patch, and content-revision."
        },
        {
          "id": "version",
          "anchor": "version",
          "heading": "Version provenance",
          "kind": "provenance",
          "required": true,
          "purpose": "Engine package version this page describes, when the API first appeared, and a warning if the live package.json has moved on.",
          "text": "Ships in @cyberart-io/engine 0.0.9. SNAPSHOT_SCHEMA_VERSION is 2; ENGINE_SNAPSHOT_RUNTIME is independent of the npm version. First appeared in 0.0.6."
        }
      ]
    },
    {
      "id": "presentation-adapter",
      "slug": "presentation-adapter",
      "route": "/docs/features/presentation-adapter",
      "title": "Presentation adapter",
      "purpose": "Host-owned render model with loading / ready / error / unsupported phases and typed intents.",
      "status": "substantial",
      "navGroup": "events-presentation",
      "packageName": "@cyberart-io/engine",
      "enginePackageVersion": "0.0.12",
      "compatibleEngineVersions": {
        "min": "0.0.6"
      },
      "documentationSource": {
        "kind": "site-page",
        "path": "src/docs/pages/presentation-adapter.ts"
      },
      "exampleIds": [],
      "relatedFeatureIds": [
        "events",
        "presentation-cue",
        "visual-layers"
      ],
      "prerequisiteFeatureIds": [
        "events"
      ],
      "aliases": [],
      "keywords": [
        "presentation-adapter",
        "attachPresentationAdapter",
        "mountPresentationAdapter"
      ],
      "surfaces": [
        "browser"
      ],
      "primaryExports": [
        "attachPresentationAdapter",
        "mountPresentationAdapter"
      ],
      "agentRoute": "/docs/agent/features/presentation-adapter.json",
      "summary": "Host-owned render model with loading / ready / error / unsupported phases and typed intents.",
      "owningModule": "./canvas/presentationAdapter",
      "lifecycle": {
        "status": "substantial",
        "introducedIn": "0.0.6",
        "compatibleEngineVersions": {
          "min": "0.0.6"
        },
        "normative": true,
        "experimental": false,
        "deprecated": false,
        "successorFeatureIds": []
      },
      "evidence": {
        "implementationPath": "packages/engine/src/canvas/presentationAdapter.ts",
        "tests": [
          "packages/engine/src/canvas/presentationAdapter.spec.ts"
        ],
        "engineGuide": "packages/engine/docs/presentation-adapter.md"
      },
      "redirectsFrom": [],
      "examples": [],
      "pageSections": [
        {
          "id": "problem",
          "anchor": "problem",
          "heading": "What this solves",
          "kind": "tutorial",
          "required": true,
          "purpose": "Name the problem the feature exists to solve.",
          "text": "Hosts need a render model with explicit phases and typed intents instead of inferring UI from cart internals."
        },
        {
          "id": "when-to-use",
          "anchor": "when-to-use",
          "heading": "When to use it",
          "kind": "tutorial",
          "required": true,
          "purpose": "Say when this is the right tool, and when a related feature is a better fit.",
          "text": "attachPresentationAdapter / mountPresentationAdapter when the host chrome must track loading, ready, error, and unsupported. Presentation-cue is the timeline; this is the host-facing phase machine."
        },
        {
          "id": "conceptual-model",
          "anchor": "conceptual-model",
          "heading": "Conceptual model",
          "kind": "tutorial",
          "required": true,
          "purpose": "Objects, states, and how they relate. Diagrams optional; names must match the API.",
          "text": "Host-owned render model; phases: loading, ready, error, unsupported. Typed intents move the model; the cart does not paint these phases; the host does."
        },
        {
          "id": "lifecycle",
          "anchor": "lifecycle",
          "heading": "Lifecycle",
          "kind": "normative",
          "required": true,
          "purpose": "Create / attach / run / snapshot / restore / destroy, including which events fire.",
          "text": "attach or mount adapter → feed runtime/cart events → host renders the phase → detach on destroy."
        },
        {
          "id": "cart-vs-host",
          "anchor": "cart-vs-host",
          "heading": "Cart versus host",
          "kind": "normative",
          "required": true,
          "purpose": "What the cart may declare or emit versus what the host/runtime must provide. Do not blur the boundary.",
          "text": "The host owns the adapter and the UI for each phase; the cart emits intents. Unsupported is a first-class phase, not a blank canvas."
        },
        {
          "id": "contract",
          "anchor": "contract",
          "heading": "Minimal contract",
          "kind": "normative",
          "required": true,
          "purpose": "Smallest API/configuration surface that a correct integration uses, with types and defaults.",
          "text": "```ts\nimport { attachPresentationAdapter } from '@cyberart-io/engine';\n\nconst adapter = attachPresentationAdapter({ /* host render model */ });\n```\n\nPrimary exports: attachPresentationAdapter, mountPresentationAdapter."
        },
        {
          "id": "failure",
          "anchor": "failure",
          "heading": "Failure and fallback",
          "kind": "normative",
          "required": true,
          "purpose": "How the feature fails closed, what events/diagnostics look like, and what the host should do.",
          "text": "Error and unsupported phases fail closed to those states; Do not treat a missing canvas as ready. Hosts must not swallow adapter errors into a hung loading spinner."
        },
        {
          "id": "accessibility",
          "anchor": "accessibility",
          "heading": "Accessibility",
          "kind": "normative",
          "required": true,
          "purpose": "Keyboard, focus, reduced motion, captions/non-audio alternatives, contrast, and anything the feature itself publishes (e.g. semantic regions).",
          "text": "Announce phase changes in text (loading, error, unsupported); Do not use color-only phase indicators. Keyboard must still reach host controls while the cart is not ready."
        },
        {
          "id": "example",
          "anchor": "example",
          "heading": "Runnable example",
          "kind": "example",
          "required": true,
          "purpose": "Link canonical example IDs from the manifest. Embed and exact source come from the lock; this section must not paste a drifting snippet.",
          "text": "No canonical example yet. packages/engine/docs/presentation-adapter.md and packages/engine/src/canvas/presentationAdapter.spec.ts are the runtime proof."
        },
        {
          "id": "expected-result",
          "anchor": "expected-result",
          "heading": "Expected result",
          "kind": "example",
          "required": true,
          "purpose": "What a reader should see, hear, or be able to do after the example runs.",
          "text": "A host that mounts the adapter can read the current phase and render loading/error/unsupported without inspecting cart state."
        },
        {
          "id": "source",
          "anchor": "source",
          "heading": "Source, tests, and version",
          "kind": "provenance",
          "required": true,
          "purpose": "Links to implementationPath, tests, engine guide, and the pinned enginePackageVersion.",
          "text": "Implementation packages/engine/src/canvas/presentationAdapter.ts. Tests: packages/engine/src/canvas/presentationAdapter.spec.ts. Guide: packages/engine/docs/presentation-adapter.md."
        },
        {
          "id": "related",
          "anchor": "related",
          "heading": "Related features",
          "kind": "tutorial",
          "required": true,
          "purpose": "Prerequisites first, then relatedFeatureIds. Use manifest ids, not free text names.",
          "text": "Prerequisite: events. Related: presentation-cue, visual-layers."
        },
        {
          "id": "advanced",
          "anchor": "advanced",
          "heading": "Advanced composition",
          "kind": "tutorial",
          "required": false,
          "purpose": "Optional: combining this feature with runtime groups, compositor, bindings, or scenarios.",
          "text": ""
        },
        {
          "id": "version",
          "anchor": "version",
          "heading": "Version provenance",
          "kind": "provenance",
          "required": true,
          "purpose": "Engine package version this page describes, when the API first appeared, and a warning if the live package.json has moved on.",
          "text": "Ships in @cyberart-io/engine 0.0.9. First appeared in 0.0.6."
        }
      ]
    },
    {
      "id": "presentation-cue",
      "slug": "presentation-cue",
      "route": "/docs/features/presentation-cue",
      "title": "Presentation cue",
      "purpose": "Deterministic createPresentationTimeline with duplicate policy, reduced-motion, and cue lifecycle events.",
      "status": "substantial",
      "navGroup": "events-presentation",
      "packageName": "@cyberart-io/engine",
      "enginePackageVersion": "0.0.12",
      "compatibleEngineVersions": {
        "min": "0.0.6"
      },
      "documentationSource": {
        "kind": "site-page",
        "path": "src/docs/pages/presentation-cue.ts"
      },
      "exampleIds": [],
      "relatedFeatureIds": [
        "presentation-adapter",
        "presentation-sequences",
        "audio"
      ],
      "prerequisiteFeatureIds": [],
      "aliases": [],
      "keywords": [
        "presentation-cue",
        "createPresentationTimeline",
        "applyCueEasing"
      ],
      "surfaces": [
        "browser"
      ],
      "primaryExports": [
        "createPresentationTimeline",
        "applyCueEasing"
      ],
      "agentRoute": "/docs/agent/features/presentation-cue.json",
      "summary": "Deterministic createPresentationTimeline with duplicate policy, reduced-motion, and cue lifecycle events.",
      "owningModule": "./canvas/presentationCue",
      "lifecycle": {
        "status": "substantial",
        "introducedIn": "0.0.6",
        "compatibleEngineVersions": {
          "min": "0.0.6"
        },
        "normative": true,
        "experimental": false,
        "deprecated": false,
        "successorFeatureIds": []
      },
      "evidence": {
        "implementationPath": "packages/engine/src/canvas/presentationCue.ts",
        "tests": [
          "packages/engine/src/canvas/presentationCue.spec.ts",
          "packages/engine/src/canvas/cyb-58-presentation-cue.repro.spec.ts"
        ],
        "engineGuide": "packages/engine/docs/presentation-cue.md"
      },
      "redirectsFrom": [],
      "examples": [],
      "pageSections": [
        {
          "id": "problem",
          "anchor": "problem",
          "heading": "What this solves",
          "kind": "tutorial",
          "required": true,
          "purpose": "Name the problem the feature exists to solve.",
          "text": "Presentation needs a deterministic timeline with duplicate policy, reduced-motion, and cue lifecycle events — not setTimeout."
        },
        {
          "id": "when-to-use",
          "anchor": "when-to-use",
          "heading": "When to use it",
          "kind": "tutorial",
          "required": true,
          "purpose": "Say when this is the right tool, and when a related feature is a better fit.",
          "text": "createPresentationTimeline / applyCueEasing for visual/caption cues; audio cues compose this via createAudioCueTimeline. Presentation-sequences coordinate several of these on one clock."
        },
        {
          "id": "conceptual-model",
          "anchor": "conceptual-model",
          "heading": "Conceptual model",
          "kind": "tutorial",
          "required": true,
          "purpose": "Objects, states, and how they relate. Diagrams optional; names must match the API.",
          "text": "Frame-stepped timeline; duplicate policy, delayFrames, easing. Lifecycle events fire as cues start/end/skip; reduced-motion is a timeline option, distinct from audio reducedSensory."
        },
        {
          "id": "lifecycle",
          "anchor": "lifecycle",
          "heading": "Lifecycle",
          "kind": "normative",
          "required": true,
          "purpose": "Create / attach / run / snapshot / restore / destroy, including which events fire.",
          "text": "createPresentationTimeline → schedule cues → step from the same clock as cart.step → destroy with the host."
        },
        {
          "id": "cart-vs-host",
          "anchor": "cart-vs-host",
          "heading": "Cart versus host",
          "kind": "normative",
          "required": true,
          "purpose": "What the cart may declare or emit versus what the host/runtime must provide. Do not blur the boundary.",
          "text": "The host (or sequence player) owns the timeline; carts may emit cue intents. Do not drive cues from requestAnimationFrame wall time in tests."
        },
        {
          "id": "contract",
          "anchor": "contract",
          "heading": "Minimal contract",
          "kind": "normative",
          "required": true,
          "purpose": "Smallest API/configuration surface that a correct integration uses, with types and defaults.",
          "text": "```ts\nimport { createPresentationTimeline, applyCueEasing } from '@cyberart-io/engine';\n\nconst timeline = createPresentationTimeline({ originFrame: 0 });\n```\n\nPrimary exports: createPresentationTimeline, applyCueEasing."
        },
        {
          "id": "failure",
          "anchor": "failure",
          "heading": "Failure and fallback",
          "kind": "normative",
          "required": true,
          "purpose": "How the feature fails closed, what events/diagnostics look like, and what the host should do.",
          "text": "Duplicate policy skips or replaces; it does not throw; reduced-motion still records lifecycle events. Missing step calls leave cues unstarted — not a silent pass in CI if you assert events."
        },
        {
          "id": "accessibility",
          "anchor": "accessibility",
          "heading": "Accessibility",
          "kind": "normative",
          "required": true,
          "purpose": "Keyboard, focus, reduced motion, captions/non-audio alternatives, contrast, and anything the feature itself publishes (e.g. semantic regions).",
          "text": "Honor reduced-motion; captions belong with sequences/audio, not only paint. Cue skips must remain inspectable."
        },
        {
          "id": "example",
          "anchor": "example",
          "heading": "Runnable example",
          "kind": "example",
          "required": true,
          "purpose": "Link canonical example IDs from the manifest. Embed and exact source come from the lock; this section must not paste a drifting snippet.",
          "text": "No canonical example yet. packages/engine/docs/presentation-cue.md and packages/engine/src/canvas/cyb-58-presentation-cue.repro.spec.ts are the runtime proof."
        },
        {
          "id": "expected-result",
          "anchor": "expected-result",
          "heading": "Expected result",
          "kind": "example",
          "required": true,
          "purpose": "What a reader should see, hear, or be able to do after the example runs.",
          "text": "Stepping the timeline emits cue lifecycle events at authored frames. reduced-motion skips motion but keeps the event record."
        },
        {
          "id": "source",
          "anchor": "source",
          "heading": "Source, tests, and version",
          "kind": "provenance",
          "required": true,
          "purpose": "Links to implementationPath, tests, engine guide, and the pinned enginePackageVersion.",
          "text": "Implementation packages/engine/src/canvas/presentationCue.ts. Tests: packages/engine/src/canvas/presentationCue.spec.ts and packages/engine/src/canvas/cyb-58-presentation-cue.repro.spec.ts. Guide: packages/engine/docs/presentation-cue.md."
        },
        {
          "id": "related",
          "anchor": "related",
          "heading": "Related features",
          "kind": "tutorial",
          "required": true,
          "purpose": "Prerequisites first, then relatedFeatureIds. Use manifest ids, not free text names.",
          "text": "Related: presentation-adapter, presentation-sequences, audio. Prerequisites: none."
        },
        {
          "id": "advanced",
          "anchor": "advanced",
          "heading": "Advanced composition",
          "kind": "tutorial",
          "required": false,
          "purpose": "Optional: combining this feature with runtime groups, compositor, bindings, or scenarios.",
          "text": ""
        },
        {
          "id": "version",
          "anchor": "version",
          "heading": "Version provenance",
          "kind": "provenance",
          "required": true,
          "purpose": "Engine package version this page describes, when the API first appeared, and a warning if the live package.json has moved on.",
          "text": "Ships in @cyberart-io/engine 0.0.9. First appeared in 0.0.6."
        }
      ]
    },
    {
      "id": "loading-indicator",
      "slug": "loading-indicator",
      "route": "/docs/features/loading-indicator",
      "title": "Loading indicator",
      "purpose": "Host-facing presentation-only loader carts with seeded variants, honest progress, snapshot/restore, and builtin fallback.",
      "status": "substantial",
      "navGroup": "events-presentation",
      "packageName": "@cyberart-io/engine",
      "enginePackageVersion": "0.0.12",
      "compatibleEngineVersions": {
        "min": "0.0.12"
      },
      "documentationSource": {
        "kind": "site-page",
        "path": "src/docs/pages/loading-indicator.ts"
      },
      "exampleIds": [],
      "relatedFeatureIds": [
        "presentation-cue",
        "presentation-adapter"
      ],
      "prerequisiteFeatureIds": [],
      "aliases": [],
      "keywords": [
        "loading-indicator",
        "createLoadingIndicatorController",
        "parseLoadingIndicatorParams",
        "parseLoadingIndicatorSnapshot",
        "loadingIndicatorEventContracts"
      ],
      "surfaces": [
        "browser",
        "headless"
      ],
      "primaryExports": [
        "createLoadingIndicatorController",
        "parseLoadingIndicatorParams",
        "parseLoadingIndicatorSnapshot",
        "loadingIndicatorEventContracts"
      ],
      "agentRoute": "/docs/agent/features/loading-indicator.json",
      "summary": "Host-facing presentation-only loader carts with seeded variants, honest progress, snapshot/restore, and builtin fallback.",
      "owningModule": "./canvas/loadingIndicator",
      "lifecycle": {
        "status": "substantial",
        "introducedIn": "0.0.12",
        "compatibleEngineVersions": {
          "min": "0.0.12"
        },
        "normative": true,
        "experimental": false,
        "deprecated": false,
        "successorFeatureIds": []
      },
      "evidence": {
        "implementationPath": "packages/engine/src/canvas/loadingIndicator.ts",
        "tests": [
          "packages/engine/src/canvas/loadingIndicator.spec.ts",
          "packages/engine/src/canvas/cyb-111-loading-indicator.repro.spec.ts"
        ],
        "engineGuide": "packages/engine/docs/loading-indicator.md"
      },
      "redirectsFrom": [],
      "examples": [],
      "pageSections": [
        {
          "id": "problem",
          "anchor": "problem",
          "heading": "What this solves",
          "kind": "tutorial",
          "required": true,
          "purpose": "Name the problem the feature exists to solve.",
          "text": "Hosts fall back to generic spinners while carts, assets, jobs, or world transitions prepare. The loader must vary by deterministic parameters without lying about progress or depending on the resource it is waiting for."
        },
        {
          "id": "when-to-use",
          "anchor": "when-to-use",
          "heading": "When to use it",
          "kind": "tutorial",
          "required": true,
          "purpose": "Say when this is the right tool, and when a related feature is a better fit.",
          "text": "createLoadingIndicatorController when a host needs a presentation-only loading cart. Do not use it to advance gameplay, accept job results, or download loader code while the host is already blocked."
        },
        {
          "id": "conceptual-model",
          "anchor": "conceptual-model",
          "heading": "Conceptual model",
          "kind": "tutorial",
          "required": true,
          "purpose": "Objects, states, and how they relate. Diagrams optional; names must match the API.",
          "text": "Validated JSON params (schemaVersion, seed, mode, optional progress/stage/context, bounded presentation) drive a registered local cart. Lifecycle is start → update → complete | fail | cancel. Snapshot/restore is fail-closed. Unknown carts fall back to a builtin indicator and host statusText."
        },
        {
          "id": "lifecycle",
          "anchor": "lifecycle",
          "heading": "Lifecycle",
          "kind": "normative",
          "required": true,
          "purpose": "Create / attach / run / snapshot / restore / destroy, including which events fire.",
          "text": "createLoadingIndicatorController → start(params) → optional update/step → complete, fail, or cancel → snapshot/restore as needed → destroy."
        },
        {
          "id": "cart-vs-host",
          "anchor": "cart-vs-host",
          "heading": "Cart versus host",
          "kind": "normative",
          "required": true,
          "purpose": "What the cart may declare or emit versus what the host/runtime must provide. Do not blur the boundary.",
          "text": "The host owns progress, stage, statusText, and cart selection. The cart is paint-only. Loader completion must not mutate authoritative host or job state."
        },
        {
          "id": "contract",
          "anchor": "contract",
          "heading": "Minimal contract",
          "kind": "normative",
          "required": true,
          "purpose": "Smallest API/configuration surface that a correct integration uses, with types and defaults.",
          "text": "```ts\nimport { createLoadingIndicatorController } from '@cyberart-io/engine';\n\nconst loading = createLoadingIndicatorController();\nloading.start({\n  seed: '0xabc',\n  mode: 'indeterminate',\n  statusText: 'Preparing travel',\n});\n```\n\nPrimary exports: createLoadingIndicatorController, parseLoadingIndicatorParams, parseLoadingIndicatorSnapshot, loadingIndicatorEventContracts."
        },
        {
          "id": "failure",
          "anchor": "failure",
          "heading": "Failure and fallback",
          "kind": "normative",
          "required": true,
          "purpose": "How the feature fails closed, what events/diagnostics look like, and what the host should do.",
          "text": "Unknown or tampered JSON fails closed before mutating live state. Cart init failure falls back immediately to the builtin indicator. Budget violations emit loading.indicator.diagnostic and keep the controller alive."
        },
        {
          "id": "accessibility",
          "anchor": "accessibility",
          "heading": "Accessibility",
          "kind": "normative",
          "required": true,
          "purpose": "Keyboard, focus, reduced motion, captions/non-audio alternatives, contrast, and anything the feature itself publishes (e.g. semantic regions).",
          "text": "statusText is host-controlled and exposed on inspect().view independently of the visual cart. Reduced-motion and motif static keep pulsePhase at 0."
        },
        {
          "id": "example",
          "anchor": "example",
          "heading": "Runnable example",
          "kind": "example",
          "required": true,
          "purpose": "Link canonical example IDs from the manifest. Embed and exact source come from the lock; this section must not paste a drifting snippet.",
          "text": "No canonical site example yet. packages/engine/docs/loading-indicator.md, packages/engine/src/canvas/fixtures/loadingPulseCart.ts, and packages/engine/src/canvas/cyb-111-loading-indicator.repro.spec.ts are the runtime proof."
        },
        {
          "id": "expected-result",
          "anchor": "expected-result",
          "heading": "Expected result",
          "kind": "example",
          "required": true,
          "purpose": "What a reader should see, hear, or be able to do after the example runs.",
          "text": "Same seed and step tape restore the same view. Determinate progress is only host-supplied. Indeterminate view.progress stays null."
        },
        {
          "id": "source",
          "anchor": "source",
          "heading": "Source, tests, and version",
          "kind": "provenance",
          "required": true,
          "purpose": "Links to implementationPath, tests, engine guide, and the pinned enginePackageVersion.",
          "text": "Implementation packages/engine/src/canvas/loadingIndicator.ts. Tests: packages/engine/src/canvas/loadingIndicator.spec.ts and packages/engine/src/canvas/cyb-111-loading-indicator.repro.spec.ts. Guide: packages/engine/docs/loading-indicator.md."
        },
        {
          "id": "related",
          "anchor": "related",
          "heading": "Related features",
          "kind": "tutorial",
          "required": true,
          "purpose": "Prerequisites first, then relatedFeatureIds. Use manifest ids, not free text names.",
          "text": "Related: presentation-cue, presentation-adapter. Prerequisites: none."
        },
        {
          "id": "advanced",
          "anchor": "advanced",
          "heading": "Advanced composition",
          "kind": "tutorial",
          "required": false,
          "purpose": "Optional: combining this feature with runtime groups, compositor, bindings, or scenarios.",
          "text": ""
        },
        {
          "id": "version",
          "anchor": "version",
          "heading": "Version provenance",
          "kind": "provenance",
          "required": true,
          "purpose": "Engine package version this page describes, when the API first appeared, and a warning if the live package.json has moved on.",
          "text": "Ships in @cyberart-io/engine 0.0.12."
        }
      ]
    },
    {
      "id": "asset-resolver",
      "slug": "asset-resolver",
      "route": "/docs/features/asset-resolver",
      "title": "Asset resolver",
      "purpose": "Host-pluggable images/audio/fonts/spritesheets with cache, preload, provenance, and silent fallbacks.",
      "status": "substantial",
      "navGroup": "assets-audio",
      "packageName": "@cyberart-io/engine",
      "enginePackageVersion": "0.0.12",
      "compatibleEngineVersions": {
        "min": "0.0.6"
      },
      "documentationSource": {
        "kind": "site-page",
        "path": "src/docs/pages/asset-resolver.ts"
      },
      "exampleIds": [],
      "relatedFeatureIds": [
        "capability-manifest",
        "audio",
        "remote-cart-manifest",
        "selection-trace"
      ],
      "prerequisiteFeatureIds": [],
      "aliases": [],
      "keywords": [
        "asset-resolver",
        "createHostedAssetResolver",
        "createDeclaredAssetResolver",
        "createAssetPreloader"
      ],
      "surfaces": [
        "browser"
      ],
      "primaryExports": [
        "createHostedAssetResolver",
        "createDeclaredAssetResolver",
        "createAssetPreloader"
      ],
      "agentRoute": "/docs/agent/features/asset-resolver.json",
      "summary": "Host-pluggable images/audio/fonts/spritesheets with cache, preload, provenance, and silent fallbacks.",
      "owningModule": "./canvas/assetResolver",
      "lifecycle": {
        "status": "substantial",
        "introducedIn": "0.0.6",
        "compatibleEngineVersions": {
          "min": "0.0.6"
        },
        "normative": true,
        "experimental": false,
        "deprecated": false,
        "successorFeatureIds": []
      },
      "evidence": {
        "implementationPath": "packages/engine/src/canvas/assetResolver.ts",
        "tests": [
          "packages/engine/src/canvas/assetResolver.spec.ts",
          "packages/engine/src/canvas/cyb-26-asset-resolver.repro.spec.ts"
        ],
        "engineGuide": "packages/engine/docs/asset-resolver.md"
      },
      "redirectsFrom": [],
      "examples": [],
      "pageSections": [
        {
          "id": "problem",
          "anchor": "problem",
          "heading": "What this solves",
          "kind": "tutorial",
          "required": true,
          "purpose": "Name the problem the feature exists to solve.",
          "text": "Images, audio, fonts, and spritesheets need a host-pluggable resolver with cache, preload, provenance, and silent fallbacks — no ambient network from the cart."
        },
        {
          "id": "when-to-use",
          "anchor": "when-to-use",
          "heading": "When to use it",
          "kind": "tutorial",
          "required": true,
          "purpose": "Say when this is the right tool, and when a related feature is a better fit.",
          "text": "createHostedAssetResolver / createDeclaredAssetResolver / createAssetPreloader when a cart declares assets; remote-cart-manifest hashes declared files. Do not fetch from cart code."
        },
        {
          "id": "conceptual-model",
          "anchor": "conceptual-model",
          "heading": "Conceptual model",
          "kind": "tutorial",
          "required": true,
          "purpose": "Objects, states, and how they relate. Diagrams optional; names must match the API.",
          "text": "Host implements load; engine caches, preloads, and emits ASSET_READY_EVENT / ASSET_FAILED_EVENT. Silent fallbacks keep the cart running; provenance is host-filled."
        },
        {
          "id": "lifecycle",
          "anchor": "lifecycle",
          "heading": "Lifecycle",
          "kind": "normative",
          "required": true,
          "purpose": "Create / attach / run / snapshot / restore / destroy, including which events fire.",
          "text": "Declare assets → create resolver → preload → ready/failed events → destroy cache with the runtime as the host specifies."
        },
        {
          "id": "cart-vs-host",
          "anchor": "cart-vs-host",
          "heading": "Cart versus host",
          "kind": "normative",
          "required": true,
          "purpose": "What the cart may declare or emit versus what the host/runtime must provide. Do not blur the boundary.",
          "text": "The cart declares ids; the host fetches or supplies bytes. The engine does not open network sockets; credentials: none in docs examples."
        },
        {
          "id": "contract",
          "anchor": "contract",
          "heading": "Minimal contract",
          "kind": "normative",
          "required": true,
          "purpose": "Smallest API/configuration surface that a correct integration uses, with types and defaults.",
          "text": "```ts\nimport { createHostedAssetResolver, createAssetPreloader } from '@cyberart-io/engine';\n\nconst resolver = createHostedAssetResolver({ load: hostLoad });\n```\n\nPrimary exports: createHostedAssetResolver, createDeclaredAssetResolver, createAssetPreloader."
        },
        {
          "id": "failure",
          "anchor": "failure",
          "heading": "Failure and fallback",
          "kind": "normative",
          "required": true,
          "purpose": "How the feature fails closed, what events/diagnostics look like, and what the host should do.",
          "text": "ASSET_FAILED_EVENT plus a silent fallback — not an uncaught fetch; Missing provenance is a host error when required. Docs embeds never mount network: nonzero examples."
        },
        {
          "id": "accessibility",
          "anchor": "accessibility",
          "heading": "Accessibility",
          "kind": "normative",
          "required": true,
          "purpose": "Keyboard, focus, reduced motion, captions/non-audio alternatives, contrast, and anything the feature itself publishes (e.g. semantic regions).",
          "text": "Failed image/audio assets need text fallback. Do not leave a blank canvas as the only failure signal."
        },
        {
          "id": "example",
          "anchor": "example",
          "heading": "Runnable example",
          "kind": "example",
          "required": true,
          "purpose": "Link canonical example IDs from the manifest. Embed and exact source come from the lock; this section must not paste a drifting snippet.",
          "text": "No canonical example yet. packages/engine/docs/asset-resolver.md and packages/engine/src/canvas/cyb-26-asset-resolver.repro.spec.ts are the runtime proof."
        },
        {
          "id": "expected-result",
          "anchor": "expected-result",
          "heading": "Expected result",
          "kind": "example",
          "required": true,
          "purpose": "What a reader should see, hear, or be able to do after the example runs.",
          "text": "A declared asset either becomes ready with host bytes or fails with ASSET_FAILED_EVENT and the configured fallback."
        },
        {
          "id": "source",
          "anchor": "source",
          "heading": "Source, tests, and version",
          "kind": "provenance",
          "required": true,
          "purpose": "Links to implementationPath, tests, engine guide, and the pinned enginePackageVersion.",
          "text": "Implementation packages/engine/src/canvas/assetResolver.ts. Tests: packages/engine/src/canvas/assetResolver.spec.ts and packages/engine/src/canvas/cyb-26-asset-resolver.repro.spec.ts. Guide: packages/engine/docs/asset-resolver.md."
        },
        {
          "id": "related",
          "anchor": "related",
          "heading": "Related features",
          "kind": "tutorial",
          "required": true,
          "purpose": "Prerequisites first, then relatedFeatureIds. Use manifest ids, not free text names.",
          "text": "Related: capability-manifest, audio, remote-cart-manifest, selection-trace. Prerequisites: none."
        },
        {
          "id": "advanced",
          "anchor": "advanced",
          "heading": "Advanced composition",
          "kind": "tutorial",
          "required": false,
          "purpose": "Optional: combining this feature with runtime groups, compositor, bindings, or scenarios.",
          "text": ""
        },
        {
          "id": "version",
          "anchor": "version",
          "heading": "Version provenance",
          "kind": "provenance",
          "required": true,
          "purpose": "Engine package version this page describes, when the API first appeared, and a warning if the live package.json has moved on.",
          "text": "Ships in @cyberart-io/engine 0.0.9. First appeared in 0.0.6."
        }
      ]
    },
    {
      "id": "audio",
      "slug": "audio",
      "route": "/docs/features/audio",
      "title": "Audio broker and cues",
      "purpose": "Shared unlock broker plus deterministic audio cue timeline. PCM is not deterministic; headless traces are.",
      "status": "substantial",
      "navGroup": "assets-audio",
      "packageName": "@cyberart-io/engine",
      "enginePackageVersion": "0.0.12",
      "compatibleEngineVersions": {
        "min": "0.0.6"
      },
      "documentationSource": {
        "kind": "site-page",
        "path": "src/docs/pages/audio.ts"
      },
      "exampleIds": [
        "audio-unlock"
      ],
      "relatedFeatureIds": [
        "asset-resolver",
        "presentation-cue",
        "presentation-sequences",
        "capability-manifest"
      ],
      "prerequisiteFeatureIds": [],
      "aliases": [],
      "keywords": [
        "audio",
        "createAudioBroker",
        "createAudioCueTimeline",
        "createHeadlessAudioAdapter"
      ],
      "surfaces": [
        "browser",
        "headless"
      ],
      "primaryExports": [
        "createAudioBroker",
        "createAudioCueTimeline",
        "createHeadlessAudioAdapter"
      ],
      "agentRoute": "/docs/agent/features/audio.json",
      "summary": "Shared unlock broker plus deterministic audio cue timeline. PCM is not deterministic; headless traces are.",
      "owningModule": "./canvas/audioBroker",
      "lifecycle": {
        "status": "substantial",
        "introducedIn": "0.0.6",
        "compatibleEngineVersions": {
          "min": "0.0.6"
        },
        "normative": true,
        "experimental": false,
        "deprecated": false,
        "successorFeatureIds": []
      },
      "evidence": {
        "implementationPath": "packages/engine/src/canvas/audioBroker.ts",
        "tests": [
          "packages/engine/src/canvas/audioBroker.spec.ts",
          "packages/engine/src/canvas/cyb-67-audio.repro.spec.ts"
        ],
        "engineGuide": "packages/engine/docs/audio.md"
      },
      "redirectsFrom": [],
      "examples": [
        {
          "id": "audio-unlock",
          "route": "/docs/examples/audio-unlock",
          "title": "Audio unlock control",
          "purpose": "Show the host unlock gesture for the shared audio graph. This cart paints a speaker glyph and emits no PCM. Run is the unlock control; the caption is the non-audio alternative.",
          "featureIds": [
            "audio"
          ],
          "runtimeKind": "render",
          "artifact": {
            "state": "available",
            "reason": "Cache verified against the lock."
          },
          "source": {
            "state": "restricted",
            "reason": "Source not published; example-carts is private.",
            "files": [
              "cart.ts",
              "main.ts",
              "manifest.json"
            ]
          },
          "provenance": {
            "repository": "cyberart-io/example-carts",
            "commit": "2ed0eb599ded827cca46bcd2fc66dea78f387a0c",
            "engineVersion": "0.0.12",
            "sourceUrl": null
          },
          "capabilities": {
            "permissions": [],
            "assets": [],
            "network": "none",
            "credentials": "none"
          },
          "interaction": {
            "mode": "none",
            "notes": "Run is the unlock control. There is no extra cart UI."
          },
          "expected": {
            "visible": "A two-rectangle speaker glyph on a dark field.",
            "audible": "Unlock click arms the shared audio graph; this cart emits no PCM. The caption is the transcript."
          },
          "accessibility": {
            "notes": "Canonical caption is expected.audible. Run is the explicit unlock control.",
            "fallback": "Text: Run unlocks the page audio graph. This example does not play a tone."
          },
          "viewport": {
            "width": 320,
            "height": 180
          }
        }
      ],
      "pageSections": [
        {
          "id": "problem",
          "anchor": "problem",
          "heading": "What this solves",
          "kind": "tutorial",
          "required": true,
          "purpose": "Name the problem the feature exists to solve.",
          "text": "Browser autoplay policy and several carts on one page make it easy to start extra AudioContexts or invent per-cart mute rules; hosts need one unlock broker and a deterministic cue trace. PCM output is not deterministic."
        },
        {
          "id": "when-to-use",
          "anchor": "when-to-use",
          "heading": "When to use it",
          "kind": "tutorial",
          "required": true,
          "purpose": "Say when this is the right tool, and when a related feature is a better fit.",
          "text": "Create one createAudioBroker per page and pass it into createRuntime / createRuntimeGroup; carts that only set metadata.audio: \"tone\" may use start()/unlockAudio() when the broker is omitted. Use presentation-cue for non-audio timelines; Do not import Tone in a silent cart."
        },
        {
          "id": "conceptual-model",
          "anchor": "conceptual-model",
          "heading": "Conceptual model",
          "kind": "tutorial",
          "required": true,
          "purpose": "Objects, states, and how they relate. Diagrams optional; names must match the API.",
          "text": "createAudioBroker owns unlock status (locked/unlocking/unlocked/failed), authorization, channel gain, mute, and ducking; createAudioCueTimeline schedules cues on the presentation-frame clock. createHeadlessAudioAdapter records cue events; it does not claim PCM identity."
        },
        {
          "id": "lifecycle",
          "anchor": "lifecycle",
          "heading": "Lifecycle",
          "kind": "normative",
          "required": true,
          "purpose": "Create / attach / run / snapshot / restore / destroy, including which events fire.",
          "text": "Host constructs the broker → authorize participant ids → unlock() inside a user gesture (or runtime.unlockAudio() from that click) → schedule cues → teardown(participantId) on destroy. destroy() on the broker tears down participants; unlock status stays unlocked and the shared context is left running."
        },
        {
          "id": "cart-vs-host",
          "anchor": "cart-vs-host",
          "heading": "Cart versus host",
          "kind": "normative",
          "required": true,
          "purpose": "What the cart may declare or emit versus what the host/runtime must provide. Do not blur the boundary.",
          "text": "The host owns the broker, unlock gesture, mute, and channel mix; the cart may declare metadata.audio: \"tone\" or emit cue intents. Silent carts never import Tone; start() only unlocks libraries the cart (or createRuntime({ audio })) declared."
        },
        {
          "id": "contract",
          "anchor": "contract",
          "heading": "Minimal contract",
          "kind": "normative",
          "required": true,
          "purpose": "Smallest API/configuration surface that a correct integration uses, with types and defaults.",
          "text": "```ts\nimport { createAudioBroker, createRuntime } from \"@cyberart-io/engine\";\n\nconst broker = createAudioBroker();\nawait broker.unlock(); // first call; later calls share status\nconst hostRuntime = createRuntime({ container: stage, audioBroker: broker });\nawait hostRuntime.unlockAudio(); // early click, before mount\n```\n\nunlock() is idempotent. reducedSensory: true skips playback and still records cue events. inspect() returns JSON-serializable status."
        },
        {
          "id": "failure",
          "anchor": "failure",
          "heading": "Failure and fallback",
          "kind": "normative",
          "required": true,
          "purpose": "How the feature fails closed, what events/diagnostics look like, and what the host should do.",
          "text": "unlock status failed if toneStart rejects; muted and reduced-sensory skip playback and emit audio.cue.skipped. Unauthorized participants skip; destroying a cart does not close Tone for remaining carts. PCM is not a CI oracle — use cue traces."
        },
        {
          "id": "accessibility",
          "anchor": "accessibility",
          "heading": "Accessibility",
          "kind": "normative",
          "required": true,
          "purpose": "Keyboard, focus, reduced motion, captions/non-audio alternatives, contrast, and anything the feature itself publishes (e.g. semantic regions).",
          "text": "Always provide a canonical caption and a silent/text fallback; docs embeds show expected.audible as the transcript and only call unlockAudio inside Run. reducedSensory must keep the event trace; Do not rely on PCM as the only feedback."
        },
        {
          "id": "example",
          "anchor": "example",
          "heading": "Runnable example",
          "kind": "example",
          "required": true,
          "purpose": "Link canonical example IDs from the manifest. Embed and exact source come from the lock; this section must not paste a drifting snippet.",
          "text": "Canonical example audio-unlock is bound in the manifest; Run is the explicit unlock control. The caption is the non-audio alternative; this page does not copy cart.ts."
        },
        {
          "id": "expected-result",
          "anchor": "expected-result",
          "heading": "Expected result",
          "kind": "example",
          "required": true,
          "purpose": "What a reader should see, hear, or be able to do after the example runs.",
          "text": "Run unlocks the shared graph; the canvas shows a speaker glyph. The caption under the frame restates that this cart emits no PCM; fallback text is sufficient when audio is unavailable."
        },
        {
          "id": "source",
          "anchor": "source",
          "heading": "Source, tests, and version",
          "kind": "provenance",
          "required": true,
          "purpose": "Links to implementationPath, tests, engine guide, and the pinned enginePackageVersion.",
          "text": "Implementation packages/engine/src/canvas/audioBroker.ts. Tests: packages/engine/src/canvas/audioBroker.spec.ts and packages/engine/src/canvas/cyb-67-audio.repro.spec.ts. Guide: packages/engine/docs/audio.md."
        },
        {
          "id": "related",
          "anchor": "related",
          "heading": "Related features",
          "kind": "tutorial",
          "required": true,
          "purpose": "Prerequisites first, then relatedFeatureIds. Use manifest ids, not free text names.",
          "text": "Related: asset-resolver, presentation-cue, presentation-sequences, capability-manifest. Prerequisites: none."
        },
        {
          "id": "advanced",
          "anchor": "advanced",
          "heading": "Advanced composition",
          "kind": "tutorial",
          "required": false,
          "purpose": "Optional: combining this feature with runtime groups, compositor, bindings, or scenarios.",
          "text": "createRuntimeGroup({ audioBroker }) authorizes each participant id. Repeat cues keep the audio record across cycles so ducking does not stick after the first loop."
        },
        {
          "id": "version",
          "anchor": "version",
          "heading": "Version provenance",
          "kind": "provenance",
          "required": true,
          "purpose": "Engine package version this page describes, when the API first appeared, and a warning if the live package.json has moved on.",
          "text": "Ships in @cyberart-io/engine 0.0.9. First appeared in 0.0.6."
        }
      ]
    },
    {
      "id": "capability-manifest",
      "slug": "capability-manifest",
      "route": "/docs/features/capability-manifest",
      "title": "Capability manifest",
      "purpose": "Versioned JSON for runtime features, phases, managers, assets, events, permissions, and integrations.",
      "status": "substantial",
      "navGroup": "assets-audio",
      "packageName": "@cyberart-io/engine",
      "enginePackageVersion": "0.0.12",
      "compatibleEngineVersions": {
        "min": "0.0.6"
      },
      "documentationSource": {
        "kind": "site-page",
        "path": "src/docs/pages/capability-manifest.ts"
      },
      "exampleIds": [
        "calculation-capability"
      ],
      "relatedFeatureIds": [
        "events",
        "asset-resolver",
        "executable-modules",
        "remote-cart-manifest"
      ],
      "prerequisiteFeatureIds": [],
      "aliases": [],
      "keywords": [
        "capability-manifest",
        "defineCapabilityManifest",
        "parseCapabilityManifest",
        "validateCapabilityManifest"
      ],
      "surfaces": [
        "browser"
      ],
      "primaryExports": [
        "defineCapabilityManifest",
        "parseCapabilityManifest",
        "validateCapabilityManifest"
      ],
      "agentRoute": "/docs/agent/features/capability-manifest.json",
      "summary": "Versioned JSON for runtime features, phases, managers, assets, events, permissions, and integrations.",
      "owningModule": "./canvas/capabilityManifest",
      "lifecycle": {
        "status": "substantial",
        "introducedIn": "0.0.6",
        "compatibleEngineVersions": {
          "min": "0.0.6"
        },
        "normative": true,
        "experimental": false,
        "deprecated": false,
        "successorFeatureIds": []
      },
      "evidence": {
        "implementationPath": "packages/engine/src/canvas/capabilityManifest.ts",
        "tests": [
          "packages/engine/src/canvas/capabilityManifest.spec.ts",
          "packages/engine/src/canvas/cyb-25-capability-manifest.repro.spec.ts"
        ],
        "engineGuide": "packages/engine/docs/capability-manifest.md"
      },
      "redirectsFrom": [],
      "examples": [
        {
          "id": "calculation-capability",
          "route": "/docs/examples/calculation-capability",
          "title": "Calculation cart with capability manifest",
          "purpose": "Show a calculation cart that updates from host-channel events without a canvas, plus a versioned engine capability manifest the host can validate. Not a game.",
          "featureIds": [
            "calculation-carts",
            "capability-manifest",
            "headless-harness"
          ],
          "runtimeKind": "calculation",
          "artifact": {
            "state": "available",
            "reason": "Cache verified against the lock."
          },
          "source": {
            "state": "restricted",
            "reason": "Source not published; example-carts is private.",
            "files": [
              "cart.ts",
              "main.ts",
              "capabilities.json",
              "manifest.json"
            ]
          },
          "provenance": {
            "repository": "cyberart-io/example-carts",
            "commit": "2ed0eb599ded827cca46bcd2fc66dea78f387a0c",
            "engineVersion": "0.0.12",
            "sourceUrl": null
          },
          "capabilities": {
            "permissions": [
              "host.intent.*",
              "host.state.*"
            ],
            "assets": [],
            "network": "none",
            "credentials": "none",
            "engineCapabilityFile": "capabilities.json"
          },
          "interaction": {
            "mode": "scripted",
            "notes": "The host dispatches host.state.scene-changed. There is no pointer or keyboard surface."
          },
          "expected": {
            "visible": "No canvas. After a scene-changed event, cart state.sceneId matches the payload and the cart emits host.intent.overlay with overlayId banner.",
            "audible": "silent"
          },
          "accessibility": {
            "notes": "No visual surface. Docs should describe the event exchange in text.",
            "fallback": "State and emitted events are the accessible representation."
          },
          "viewport": {
            "width": 320,
            "height": 180
          }
        }
      ],
      "pageSections": [
        {
          "id": "problem",
          "anchor": "problem",
          "heading": "What this solves",
          "kind": "tutorial",
          "required": true,
          "purpose": "Name the problem the feature exists to solve.",
          "text": "Hosts need a versioned declaration of what a cart requires — runtime features, phases, managers, assets, events, permissions, integrations — before mount."
        },
        {
          "id": "when-to-use",
          "anchor": "when-to-use",
          "heading": "When to use it",
          "kind": "tutorial",
          "required": true,
          "purpose": "Say when this is the right tool, and when a related feature is a better fit.",
          "text": "defineCapabilityManifest / parseCapabilityManifest / validateCapabilityManifest for the JSON document; Use remote-cart-manifest for signed catalogs of carts. Do not treat this JSON as executable code."
        },
        {
          "id": "conceptual-model",
          "anchor": "conceptual-model",
          "heading": "Conceptual model",
          "kind": "tutorial",
          "required": true,
          "purpose": "Objects, states, and how they relate. Diagrams optional; names must match the API.",
          "text": "A capability document lists contractVersion, features, integrations, kinds, emit, and subscribe; the host compares it to what it actually provides. engineCapabilityFile on an example manifest points at that JSON."
        },
        {
          "id": "lifecycle",
          "anchor": "lifecycle",
          "heading": "Lifecycle",
          "kind": "normative",
          "required": true,
          "purpose": "Create / attach / run / snapshot / restore / destroy, including which events fire.",
          "text": "Author JSON → parseCapabilityManifest → validateCapabilityManifest(hostCapabilities) → mount only when ok. Fail closed; do not mount on a partial match."
        },
        {
          "id": "cart-vs-host",
          "anchor": "cart-vs-host",
          "heading": "Cart versus host",
          "kind": "normative",
          "required": true,
          "purpose": "What the cart may declare or emit versus what the host/runtime must provide. Do not blur the boundary.",
          "text": "The cart (or example) declares the document; the host implements the managers and permissions. The engine validates; it does not fetch the file."
        },
        {
          "id": "contract",
          "anchor": "contract",
          "heading": "Minimal contract",
          "kind": "normative",
          "required": true,
          "purpose": "Smallest API/configuration surface that a correct integration uses, with types and defaults.",
          "text": "```ts\nimport { parseCapabilityManifest, validateCapabilityManifest } from '@cyberart-io/engine';\n\nconst parsed = parseCapabilityManifest(json);\nif (!parsed.ok) throw new Error(parsed.errors.map((e) => e.detail).join('; '));\nconst check = validateCapabilityManifest(parsed.manifest, host);\n```\n\nReturns { ok: true } or { ok: false, errors }. Does not throw."
        },
        {
          "id": "failure",
          "anchor": "failure",
          "heading": "Failure and fallback",
          "kind": "normative",
          "required": true,
          "purpose": "How the feature fails closed, what events/diagnostics look like, and what the host should do.",
          "text": "Invalid JSON, unknown fields, and host mismatches fail closed. A cart that emits host.intent.* on a host that does not subscribe must not mount."
        },
        {
          "id": "accessibility",
          "anchor": "accessibility",
          "heading": "Accessibility",
          "kind": "normative",
          "required": true,
          "purpose": "Keyboard, focus, reduced motion, captions/non-audio alternatives, contrast, and anything the feature itself publishes (e.g. semantic regions).",
          "text": "The document is data; surface validation errors in text. The bound example has no visual surface; state and events are the accessible representation."
        },
        {
          "id": "example",
          "anchor": "example",
          "heading": "Runnable example",
          "kind": "example",
          "required": true,
          "purpose": "Link canonical example IDs from the manifest. Embed and exact source come from the lock; this section must not paste a drifting snippet.",
          "text": "Canonical example calculation-capability includes capabilities.json and is bound here. This page does not copy that file."
        },
        {
          "id": "expected-result",
          "anchor": "expected-result",
          "heading": "Expected result",
          "kind": "example",
          "required": true,
          "purpose": "What a reader should see, hear, or be able to do after the example runs.",
          "text": "parseCapabilityManifest succeeds and validateCapabilityManifest accepts a host that provides router, calculation kind, and the declared emit/subscribe patterns."
        },
        {
          "id": "source",
          "anchor": "source",
          "heading": "Source, tests, and version",
          "kind": "provenance",
          "required": true,
          "purpose": "Links to implementationPath, tests, engine guide, and the pinned enginePackageVersion.",
          "text": "Implementation packages/engine/src/canvas/capabilityManifest.ts. Tests: packages/engine/src/canvas/capabilityManifest.spec.ts and packages/engine/src/canvas/cyb-25-capability-manifest.repro.spec.ts. Guide: packages/engine/docs/capability-manifest.md."
        },
        {
          "id": "related",
          "anchor": "related",
          "heading": "Related features",
          "kind": "tutorial",
          "required": true,
          "purpose": "Prerequisites first, then relatedFeatureIds. Use manifest ids, not free text names.",
          "text": "Related: events, asset-resolver, executable-modules, remote-cart-manifest. Prerequisites: none."
        },
        {
          "id": "advanced",
          "anchor": "advanced",
          "heading": "Advanced composition",
          "kind": "tutorial",
          "required": false,
          "purpose": "Optional: combining this feature with runtime groups, compositor, bindings, or scenarios.",
          "text": ""
        },
        {
          "id": "version",
          "anchor": "version",
          "heading": "Version provenance",
          "kind": "provenance",
          "required": true,
          "purpose": "Engine package version this page describes, when the API first appeared, and a warning if the live package.json has moved on.",
          "text": "Ships in @cyberart-io/engine 0.0.9. First appeared in 0.0.6."
        }
      ]
    },
    {
      "id": "executable-modules",
      "slug": "executable-modules",
      "route": "/docs/features/executable-modules",
      "title": "Executable modules",
      "purpose": "Trusted versioned factories, host allowlists, isolation, and per-module failures.",
      "status": "substantial",
      "navGroup": "world-content",
      "packageName": "@cyberart-io/engine",
      "enginePackageVersion": "0.0.12",
      "compatibleEngineVersions": {
        "min": "0.0.6"
      },
      "documentationSource": {
        "kind": "site-page",
        "path": "src/docs/pages/executable-modules.ts"
      },
      "exampleIds": [],
      "relatedFeatureIds": [
        "capability-manifest",
        "remote-cart-manifest"
      ],
      "prerequisiteFeatureIds": [],
      "aliases": [],
      "keywords": [
        "executable-modules",
        "createExecutableModuleHost",
        "freezeCapabilityBag"
      ],
      "surfaces": [
        "browser"
      ],
      "primaryExports": [
        "createExecutableModuleHost",
        "freezeCapabilityBag"
      ],
      "agentRoute": "/docs/agent/features/executable-modules.json",
      "summary": "Trusted versioned factories, host allowlists, isolation, and per-module failures.",
      "owningModule": "./canvas/executableModule",
      "lifecycle": {
        "status": "substantial",
        "introducedIn": "0.0.6",
        "compatibleEngineVersions": {
          "min": "0.0.6"
        },
        "normative": true,
        "experimental": false,
        "deprecated": false,
        "successorFeatureIds": []
      },
      "evidence": {
        "implementationPath": "packages/engine/src/canvas/executableModule.ts",
        "tests": [
          "packages/engine/src/canvas/executableModule.spec.ts",
          "packages/engine/src/canvas/cyb-27-executable-module.repro.spec.ts"
        ],
        "engineGuide": "packages/engine/docs/executable-modules.md"
      },
      "redirectsFrom": [],
      "examples": [],
      "pageSections": [
        {
          "id": "problem",
          "anchor": "problem",
          "heading": "What this solves",
          "kind": "tutorial",
          "required": true,
          "purpose": "Name the problem the feature exists to solve.",
          "text": "Trusted versioned factories need host allowlists, isolation, and per-module failures so a cart cannot load arbitrary code."
        },
        {
          "id": "when-to-use",
          "anchor": "when-to-use",
          "heading": "When to use it",
          "kind": "tutorial",
          "required": true,
          "purpose": "Say when this is the right tool, and when a related feature is a better fit.",
          "text": "createExecutableModuleHost / freezeCapabilityBag when the host loads versioned modules; remote-cart-manifest is the signed catalog. Do not eval cart strings."
        },
        {
          "id": "conceptual-model",
          "anchor": "conceptual-model",
          "heading": "Conceptual model",
          "kind": "tutorial",
          "required": true,
          "purpose": "Objects, states, and how they relate. Diagrams optional; names must match the API.",
          "text": "Host allowlists factories by id/version; isolation: one module failure does not take down the host. freezeCapabilityBag seals what a module may see."
        },
        {
          "id": "lifecycle",
          "anchor": "lifecycle",
          "heading": "Lifecycle",
          "kind": "normative",
          "required": true,
          "purpose": "Create / attach / run / snapshot / restore / destroy, including which events fire.",
          "text": "createExecutableModuleHost({ allowlist }) → load(id, version) → run factory → per-module error boundary → unload."
        },
        {
          "id": "cart-vs-host",
          "anchor": "cart-vs-host",
          "heading": "Cart versus host",
          "kind": "normative",
          "required": true,
          "purpose": "What the cart may declare or emit versus what the host/runtime must provide. Do not blur the boundary.",
          "text": "The host owns the allowlist and isolation; modules receive a frozen capability bag. Unknown ids never run."
        },
        {
          "id": "contract",
          "anchor": "contract",
          "heading": "Minimal contract",
          "kind": "normative",
          "required": true,
          "purpose": "Smallest API/configuration surface that a correct integration uses, with types and defaults.",
          "text": "```ts\nimport { createExecutableModuleHost, freezeCapabilityBag } from '@cyberart-io/engine';\n\nconst host = createExecutableModuleHost({ /* allowlist */ });\n```\n\nPrimary exports: createExecutableModuleHost, freezeCapabilityBag."
        },
        {
          "id": "failure",
          "anchor": "failure",
          "heading": "Failure and fallback",
          "kind": "normative",
          "required": true,
          "purpose": "How the feature fails closed, what events/diagnostics look like, and what the host should do.",
          "text": "Unknown or disallowed modules fail closed; per-module failures are isolated. Do not treat a thrown factory as a host crash unless the host chooses to."
        },
        {
          "id": "accessibility",
          "anchor": "accessibility",
          "heading": "Accessibility",
          "kind": "normative",
          "required": true,
          "purpose": "Keyboard, focus, reduced motion, captions/non-audio alternatives, contrast, and anything the feature itself publishes (e.g. semantic regions).",
          "text": "Module load errors belong in host UI text. Do not hide a failed module behind a blank overlay without a name."
        },
        {
          "id": "example",
          "anchor": "example",
          "heading": "Runnable example",
          "kind": "example",
          "required": true,
          "purpose": "Link canonical example IDs from the manifest. Embed and exact source come from the lock; this section must not paste a drifting snippet.",
          "text": "No canonical example yet. packages/engine/docs/executable-modules.md and packages/engine/src/canvas/cyb-27-executable-module.repro.spec.ts are the runtime proof."
        },
        {
          "id": "expected-result",
          "anchor": "expected-result",
          "heading": "Expected result",
          "kind": "example",
          "required": true,
          "purpose": "What a reader should see, hear, or be able to do after the example runs.",
          "text": "An allowlisted factory runs; an unknown id does not. freezeCapabilityBag prevents accidental mutation of the bag."
        },
        {
          "id": "source",
          "anchor": "source",
          "heading": "Source, tests, and version",
          "kind": "provenance",
          "required": true,
          "purpose": "Links to implementationPath, tests, engine guide, and the pinned enginePackageVersion.",
          "text": "Implementation packages/engine/src/canvas/executableModule.ts. Tests: packages/engine/src/canvas/executableModule.spec.ts and packages/engine/src/canvas/cyb-27-executable-module.repro.spec.ts. Guide: packages/engine/docs/executable-modules.md."
        },
        {
          "id": "related",
          "anchor": "related",
          "heading": "Related features",
          "kind": "tutorial",
          "required": true,
          "purpose": "Prerequisites first, then relatedFeatureIds. Use manifest ids, not free text names.",
          "text": "Related: capability-manifest, remote-cart-manifest. Prerequisites: none."
        },
        {
          "id": "advanced",
          "anchor": "advanced",
          "heading": "Advanced composition",
          "kind": "tutorial",
          "required": false,
          "purpose": "Optional: combining this feature with runtime groups, compositor, bindings, or scenarios.",
          "text": ""
        },
        {
          "id": "version",
          "anchor": "version",
          "heading": "Version provenance",
          "kind": "provenance",
          "required": true,
          "purpose": "Engine package version this page describes, when the API first appeared, and a warning if the live package.json has moved on.",
          "text": "Ships in @cyberart-io/engine 0.0.9. First appeared in 0.0.6."
        }
      ]
    },
    {
      "id": "normalized-geometry",
      "slug": "normalized-geometry",
      "route": "/docs/features/normalized-geometry",
      "title": "Normalized geometry",
      "purpose": "Coordinate spaces, contain/cover/crop layout, landmarks, hit regions, and a debug overlay.",
      "status": "substantial",
      "navGroup": "layout",
      "packageName": "@cyberart-io/engine",
      "enginePackageVersion": "0.0.12",
      "compatibleEngineVersions": {
        "min": "0.0.6"
      },
      "documentationSource": {
        "kind": "site-page",
        "path": "src/docs/pages/normalized-geometry.ts"
      },
      "exampleIds": [],
      "relatedFeatureIds": [
        "pointer",
        "semantic-layers",
        "compositor",
        "runtime"
      ],
      "prerequisiteFeatureIds": [],
      "aliases": [],
      "keywords": [
        "normalized-geometry",
        "createPresentationLayout",
        "createLandmarkRegistry",
        "validateGeometry"
      ],
      "surfaces": [
        "browser"
      ],
      "primaryExports": [
        "createPresentationLayout",
        "createLandmarkRegistry",
        "validateGeometry"
      ],
      "agentRoute": "/docs/agent/features/normalized-geometry.json",
      "summary": "Coordinate spaces, contain/cover/crop layout, landmarks, hit regions, and a debug overlay.",
      "owningModule": "./canvas/normalizedGeometry",
      "lifecycle": {
        "status": "substantial",
        "introducedIn": "0.0.6",
        "compatibleEngineVersions": {
          "min": "0.0.6"
        },
        "normative": true,
        "experimental": false,
        "deprecated": false,
        "successorFeatureIds": []
      },
      "evidence": {
        "implementationPath": "packages/engine/src/canvas/normalizedGeometry.ts",
        "tests": [
          "packages/engine/src/canvas/normalizedGeometry.spec.ts",
          "packages/engine/src/canvas/cyb-60-normalized-geometry.repro.spec.ts"
        ],
        "engineGuide": "packages/engine/docs/normalized-geometry.md"
      },
      "redirectsFrom": [],
      "examples": [],
      "pageSections": [
        {
          "id": "problem",
          "anchor": "problem",
          "heading": "What this solves",
          "kind": "tutorial",
          "required": true,
          "purpose": "Name the problem the feature exists to solve.",
          "text": "Carts and hosts need coordinate spaces, contain/cover/crop layout, landmarks, and hit regions that survive resize without each cart inventing a transform."
        },
        {
          "id": "when-to-use",
          "anchor": "when-to-use",
          "heading": "When to use it",
          "kind": "tutorial",
          "required": true,
          "purpose": "Say when this is the right tool, and when a related feature is a better fit.",
          "text": "createPresentationLayout / createLandmarkRegistry / validateGeometry when drawing or hit-testing in a design space; Pointer uses canvas pixels unless you convert. Semantic-layers name regions on top."
        },
        {
          "id": "conceptual-model",
          "anchor": "conceptual-model",
          "heading": "Conceptual model",
          "kind": "tutorial",
          "required": true,
          "purpose": "Objects, states, and how they relate. Diagrams optional; names must match the API.",
          "text": "A layout maps normalized/design coordinates to canvas via contain, cover, or crop; landmarks are named points. Hit regions are testable shapes; debug overlay is optional."
        },
        {
          "id": "lifecycle",
          "anchor": "lifecycle",
          "heading": "Lifecycle",
          "kind": "normative",
          "required": true,
          "purpose": "Create / attach / run / snapshot / restore / destroy, including which events fire.",
          "text": "createPresentationLayout from container size → validateGeometry → hit-test / draw in layout space → recompute on resize."
        },
        {
          "id": "cart-vs-host",
          "anchor": "cart-vs-host",
          "heading": "Cart versus host",
          "kind": "normative",
          "required": true,
          "purpose": "What the cart may declare or emit versus what the host/runtime must provide. Do not blur the boundary.",
          "text": "The host owns the container size and whether the debug overlay is on. The cart draws in the space the layout describes."
        },
        {
          "id": "contract",
          "anchor": "contract",
          "heading": "Minimal contract",
          "kind": "normative",
          "required": true,
          "purpose": "Smallest API/configuration surface that a correct integration uses, with types and defaults.",
          "text": "```ts\nimport { createPresentationLayout, createLandmarkRegistry, validateGeometry } from '@cyberart-io/engine';\n\nconst layout = createPresentationLayout({ width, height, mode: 'contain' });\n```\n\nPrimary exports: createPresentationLayout, createLandmarkRegistry, validateGeometry."
        },
        {
          "id": "failure",
          "anchor": "failure",
          "heading": "Failure and fallback",
          "kind": "normative",
          "required": true,
          "purpose": "How the feature fails closed, what events/diagnostics look like, and what the host should do.",
          "text": "validateGeometry fails closed on invalid shapes; cover/crop may clip — that is layout, not an error. Do not mix CSS pixels and canvas pixels without converting."
        },
        {
          "id": "accessibility",
          "anchor": "accessibility",
          "heading": "Accessibility",
          "kind": "normative",
          "required": true,
          "purpose": "Keyboard, focus, reduced motion, captions/non-audio alternatives, contrast, and anything the feature itself publishes (e.g. semantic regions).",
          "text": "Landmarks and hit regions should pair with semantic-layers names/roles. Debug overlay is not an a11y surface."
        },
        {
          "id": "example",
          "anchor": "example",
          "heading": "Runnable example",
          "kind": "example",
          "required": true,
          "purpose": "Link canonical example IDs from the manifest. Embed and exact source come from the lock; this section must not paste a drifting snippet.",
          "text": "No canonical example yet. packages/engine/docs/normalized-geometry.md and packages/engine/src/canvas/cyb-60-normalized-geometry.repro.spec.ts are the runtime proof."
        },
        {
          "id": "expected-result",
          "anchor": "expected-result",
          "heading": "Expected result",
          "kind": "example",
          "required": true,
          "purpose": "What a reader should see, hear, or be able to do after the example runs.",
          "text": "The same landmark maps to stable canvas coordinates for a given layout mode and size. Invalid geometry is rejected."
        },
        {
          "id": "source",
          "anchor": "source",
          "heading": "Source, tests, and version",
          "kind": "provenance",
          "required": true,
          "purpose": "Links to implementationPath, tests, engine guide, and the pinned enginePackageVersion.",
          "text": "Implementation packages/engine/src/canvas/normalizedGeometry.ts. Tests: packages/engine/src/canvas/normalizedGeometry.spec.ts and packages/engine/src/canvas/cyb-60-normalized-geometry.repro.spec.ts. Guide: packages/engine/docs/normalized-geometry.md."
        },
        {
          "id": "related",
          "anchor": "related",
          "heading": "Related features",
          "kind": "tutorial",
          "required": true,
          "purpose": "Prerequisites first, then relatedFeatureIds. Use manifest ids, not free text names.",
          "text": "Related: pointer, semantic-layers, compositor, runtime. Prerequisites: none."
        },
        {
          "id": "advanced",
          "anchor": "advanced",
          "heading": "Advanced composition",
          "kind": "tutorial",
          "required": false,
          "purpose": "Optional: combining this feature with runtime groups, compositor, bindings, or scenarios.",
          "text": ""
        },
        {
          "id": "version",
          "anchor": "version",
          "heading": "Version provenance",
          "kind": "provenance",
          "required": true,
          "purpose": "Engine package version this page describes, when the API first appeared, and a warning if the live package.json has moved on.",
          "text": "Ships in @cyberart-io/engine 0.0.9. First appeared in 0.0.6."
        }
      ]
    },
    {
      "id": "runtime-group",
      "slug": "runtime-group",
      "route": "/docs/features/runtime-group",
      "title": "Runtime group",
      "purpose": "createRuntimeGroup: several carts, shared router attach, lockstep clock. Headless twin is createHeadlessMultiCartHarness.",
      "status": "substantial",
      "navGroup": "composition",
      "packageName": "@cyberart-io/engine",
      "enginePackageVersion": "0.0.12",
      "compatibleEngineVersions": {
        "min": "0.0.6"
      },
      "documentationSource": {
        "kind": "site-page",
        "path": "src/docs/pages/runtime-group.ts"
      },
      "exampleIds": [],
      "relatedFeatureIds": [
        "runtime",
        "calculation-carts",
        "compositor",
        "portal-lifecycle",
        "headless-harness"
      ],
      "prerequisiteFeatureIds": [
        "runtime",
        "events"
      ],
      "aliases": [],
      "keywords": [
        "runtime-group",
        "createRuntimeGroup"
      ],
      "surfaces": [
        "browser",
        "headless"
      ],
      "primaryExports": [
        "createRuntimeGroup"
      ],
      "agentRoute": "/docs/agent/features/runtime-group.json",
      "summary": "createRuntimeGroup: several carts, shared router attach, lockstep clock. Headless twin is createHeadlessMultiCartHarness.",
      "owningModule": "./canvas/runtimeGroup",
      "lifecycle": {
        "status": "substantial",
        "introducedIn": "0.0.6",
        "compatibleEngineVersions": {
          "min": "0.0.6"
        },
        "normative": true,
        "experimental": false,
        "deprecated": false,
        "successorFeatureIds": []
      },
      "evidence": {
        "implementationPath": "packages/engine/src/canvas/runtimeGroup.ts",
        "tests": [
          "packages/engine/src/canvas/runtimeGroup.spec.ts",
          "packages/engine/src/canvas/headlessMultiCartHarness.spec.ts",
          "packages/engine/src/canvas/cyb-61-runtime-group.repro.spec.ts"
        ],
        "engineGuide": "packages/engine/docs/runtime-group.md"
      },
      "redirectsFrom": [],
      "examples": [],
      "pageSections": [
        {
          "id": "problem",
          "anchor": "problem",
          "heading": "What this solves",
          "kind": "tutorial",
          "required": true,
          "purpose": "Name the problem the feature exists to solve.",
          "text": "Several carts on one page need a shared router attach and a lockstep clock without each inventing a coordinator."
        },
        {
          "id": "when-to-use",
          "anchor": "when-to-use",
          "heading": "When to use it",
          "kind": "tutorial",
          "required": true,
          "purpose": "Say when this is the right tool, and when a related feature is a better fit.",
          "text": "createRuntimeGroup when more than one cart must tick together; single-cart pages use createRuntime. Headless twin: createHeadlessMultiCartHarness."
        },
        {
          "id": "conceptual-model",
          "anchor": "conceptual-model",
          "heading": "Conceptual model",
          "kind": "tutorial",
          "required": true,
          "purpose": "Objects, states, and how they relate. Diagrams optional; names must match the API.",
          "text": "createRuntimeGroup({ participants, audioBroker; }). Shared router attach; lockstep clock. Calculation participants share the group without a canvas; compositor stacks their frames."
        },
        {
          "id": "lifecycle",
          "anchor": "lifecycle",
          "heading": "Lifecycle",
          "kind": "normative",
          "required": true,
          "purpose": "Create / attach / run / snapshot / restore / destroy, including which events fire.",
          "text": "createRuntimeGroup → start/step the group → teardown participants → destroy. Audio broker authorize/teardown follows participant ids."
        },
        {
          "id": "cart-vs-host",
          "anchor": "cart-vs-host",
          "heading": "Cart versus host",
          "kind": "normative",
          "required": true,
          "purpose": "What the cart may declare or emit versus what the host/runtime must provide. Do not blur the boundary.",
          "text": "The host owns the group, participant ids, and which carts are calculation vs render. Carts stay unaware of siblings except through the router."
        },
        {
          "id": "contract",
          "anchor": "contract",
          "heading": "Minimal contract",
          "kind": "normative",
          "required": true,
          "purpose": "Smallest API/configuration surface that a correct integration uses, with types and defaults.",
          "text": "```ts\nimport { createRuntimeGroup } from '@cyberart-io/engine';\n\nconst group = createRuntimeGroup({ participants: [/* … */] });\n```\n\nPrimary export: createRuntimeGroup. Headless: createHeadlessMultiCartHarness from @cyberart-io/engine/headless."
        },
        {
          "id": "failure",
          "anchor": "failure",
          "heading": "Failure and fallback",
          "kind": "normative",
          "required": true,
          "purpose": "How the feature fails closed, what events/diagnostics look like, and what the host should do.",
          "text": "A failed participant must not silently stall the group unless the host says so; destroy is per participant plus the group. Do not mix wall clocks across members."
        },
        {
          "id": "accessibility",
          "anchor": "accessibility",
          "heading": "Accessibility",
          "kind": "normative",
          "required": true,
          "purpose": "Keyboard, focus, reduced motion, captions/non-audio alternatives, contrast, and anything the feature itself publishes (e.g. semantic regions).",
          "text": "Multiple canvases need host labels. Calculation members have no visual; describe their events in text."
        },
        {
          "id": "example",
          "anchor": "example",
          "heading": "Runnable example",
          "kind": "example",
          "required": true,
          "purpose": "Link canonical example IDs from the manifest. Embed and exact source come from the lock; this section must not paste a drifting snippet.",
          "text": "No canonical example yet. packages/engine/docs/runtime-group.md and packages/engine/src/canvas/cyb-61-runtime-group.repro.spec.ts are the runtime proof."
        },
        {
          "id": "expected-result",
          "anchor": "expected-result",
          "heading": "Expected result",
          "kind": "example",
          "required": true,
          "purpose": "What a reader should see, hear, or be able to do after the example runs.",
          "text": "Stepping the group advances every participant on the same clock. Calculation members still have no canvas."
        },
        {
          "id": "source",
          "anchor": "source",
          "heading": "Source, tests, and version",
          "kind": "provenance",
          "required": true,
          "purpose": "Links to implementationPath, tests, engine guide, and the pinned enginePackageVersion.",
          "text": "Implementation packages/engine/src/canvas/runtimeGroup.ts. Tests: runtimeGroup.spec.ts, headlessMultiCartHarness.spec.ts, cyb-61-runtime-group.repro.spec.ts. Guide: packages/engine/docs/runtime-group.md."
        },
        {
          "id": "related",
          "anchor": "related",
          "heading": "Related features",
          "kind": "tutorial",
          "required": true,
          "purpose": "Prerequisites first, then relatedFeatureIds. Use manifest ids, not free text names.",
          "text": "Prerequisites: runtime, events. Related: calculation-carts, compositor, portal-lifecycle, headless-harness."
        },
        {
          "id": "advanced",
          "anchor": "advanced",
          "heading": "Advanced composition",
          "kind": "tutorial",
          "required": false,
          "purpose": "Optional: combining this feature with runtime groups, compositor, bindings, or scenarios.",
          "text": "Pass audioBroker so one unlock covers authorized participant ids."
        },
        {
          "id": "version",
          "anchor": "version",
          "heading": "Version provenance",
          "kind": "provenance",
          "required": true,
          "purpose": "Engine package version this page describes, when the API first appeared, and a warning if the live package.json has moved on.",
          "text": "Ships in @cyberart-io/engine 0.0.9. First appeared in 0.0.6."
        }
      ]
    },
    {
      "id": "compositor",
      "slug": "compositor",
      "route": "/docs/features/compositor",
      "title": "Compositor",
      "purpose": "Transparent multi-cart stacking, blend modes, and captureComposedFrame / writeComposedFrame.",
      "status": "substantial",
      "navGroup": "composition",
      "packageName": "@cyberart-io/engine",
      "enginePackageVersion": "0.0.12",
      "compatibleEngineVersions": {
        "min": "0.0.6"
      },
      "documentationSource": {
        "kind": "site-page",
        "path": "src/docs/pages/compositor.ts"
      },
      "exampleIds": [],
      "relatedFeatureIds": [
        "runtime-group",
        "visual-layers",
        "normalized-geometry"
      ],
      "prerequisiteFeatureIds": [
        "runtime-group"
      ],
      "aliases": [],
      "keywords": [
        "compositor",
        "createCompositor"
      ],
      "surfaces": [
        "browser",
        "headless"
      ],
      "primaryExports": [
        "createCompositor"
      ],
      "agentRoute": "/docs/agent/features/compositor.json",
      "summary": "Transparent multi-cart stacking, blend modes, and captureComposedFrame / writeComposedFrame.",
      "owningModule": "./canvas/compositor",
      "lifecycle": {
        "status": "substantial",
        "introducedIn": "0.0.6",
        "compatibleEngineVersions": {
          "min": "0.0.6"
        },
        "normative": true,
        "experimental": false,
        "deprecated": false,
        "successorFeatureIds": []
      },
      "evidence": {
        "implementationPath": "packages/engine/src/canvas/compositor.ts",
        "tests": [
          "packages/engine/src/canvas/compositor.spec.ts",
          "packages/engine/src/canvas/cyb-64-compositor.repro.spec.ts"
        ],
        "engineGuide": "packages/engine/docs/compositor.md"
      },
      "redirectsFrom": [],
      "examples": [],
      "pageSections": [
        {
          "id": "problem",
          "anchor": "problem",
          "heading": "What this solves",
          "kind": "tutorial",
          "required": true,
          "purpose": "Name the problem the feature exists to solve.",
          "text": "Multi-cart stacking needs transparent layers, blend modes, and a composed capture without flattening too early in each cart."
        },
        {
          "id": "when-to-use",
          "anchor": "when-to-use",
          "heading": "When to use it",
          "kind": "tutorial",
          "required": true,
          "purpose": "Say when this is the right tool, and when a related feature is a better fit.",
          "text": "createCompositor with a runtime group; visual-layers is overlay versions on one cart. Do not screenshot each canvas in a loop unless you need per-member debug."
        },
        {
          "id": "conceptual-model",
          "anchor": "conceptual-model",
          "heading": "Conceptual model",
          "kind": "tutorial",
          "required": true,
          "purpose": "Objects, states, and how they relate. Diagrams optional; names must match the API.",
          "text": "Transparent multi-cart stacking, blend modes (including screen), captureComposedFrame / writeComposedFrame."
        },
        {
          "id": "lifecycle",
          "anchor": "lifecycle",
          "heading": "Lifecycle",
          "kind": "normative",
          "required": true,
          "purpose": "Create / attach / run / snapshot / restore / destroy, including which events fire.",
          "text": "createCompositor → attach group members → composite each frame → capture or write → destroy with the group."
        },
        {
          "id": "cart-vs-host",
          "anchor": "cart-vs-host",
          "heading": "Cart versus host",
          "kind": "normative",
          "required": true,
          "purpose": "What the cart may declare or emit versus what the host/runtime must provide. Do not blur the boundary.",
          "text": "The host owns compositor and blend choices; carts paint their own canvas. Capture is host-side."
        },
        {
          "id": "contract",
          "anchor": "contract",
          "heading": "Minimal contract",
          "kind": "normative",
          "required": true,
          "purpose": "Smallest API/configuration surface that a correct integration uses, with types and defaults.",
          "text": "```ts\nimport { createCompositor } from '@cyberart-io/engine';\n\nconst compositor = createCompositor({ /* members */ });\nconst frame = compositor.captureComposedFrame();\n```\n\nPrimary export: createCompositor."
        },
        {
          "id": "failure",
          "anchor": "failure",
          "heading": "Failure and fallback",
          "kind": "normative",
          "required": true,
          "purpose": "How the feature fails closed, what events/diagnostics look like, and what the host should do.",
          "text": "A missing member canvas cannot be composed as if it were present; writeComposedFrame fails closed on encode errors. Order and blend mode are part of the contract — do not reorder silently."
        },
        {
          "id": "accessibility",
          "anchor": "accessibility",
          "heading": "Accessibility",
          "kind": "normative",
          "required": true,
          "purpose": "Keyboard, focus, reduced motion, captions/non-audio alternatives, contrast, and anything the feature itself publishes (e.g. semantic regions).",
          "text": "The composed bitmap is not accessible by itself; keep semantic-layers / captions on the host. Prefer reduced-motion over disabling composite."
        },
        {
          "id": "example",
          "anchor": "example",
          "heading": "Runnable example",
          "kind": "example",
          "required": true,
          "purpose": "Link canonical example IDs from the manifest. Embed and exact source come from the lock; this section must not paste a drifting snippet.",
          "text": "No canonical example yet. packages/engine/docs/compositor.md and packages/engine/src/canvas/cyb-64-compositor.repro.spec.ts are the runtime proof."
        },
        {
          "id": "expected-result",
          "anchor": "expected-result",
          "heading": "Expected result",
          "kind": "example",
          "required": true,
          "purpose": "What a reader should see, hear, or be able to do after the example runs.",
          "text": "captureComposedFrame returns one image of the stacked members with the configured blend modes."
        },
        {
          "id": "source",
          "anchor": "source",
          "heading": "Source, tests, and version",
          "kind": "provenance",
          "required": true,
          "purpose": "Links to implementationPath, tests, engine guide, and the pinned enginePackageVersion.",
          "text": "Implementation packages/engine/src/canvas/compositor.ts. Tests: packages/engine/src/canvas/compositor.spec.ts and packages/engine/src/canvas/cyb-64-compositor.repro.spec.ts. Guide: packages/engine/docs/compositor.md."
        },
        {
          "id": "related",
          "anchor": "related",
          "heading": "Related features",
          "kind": "tutorial",
          "required": true,
          "purpose": "Prerequisites first, then relatedFeatureIds. Use manifest ids, not free text names.",
          "text": "Prerequisite: runtime-group. Related: visual-layers, normalized-geometry."
        },
        {
          "id": "advanced",
          "anchor": "advanced",
          "heading": "Advanced composition",
          "kind": "tutorial",
          "required": false,
          "purpose": "Optional: combining this feature with runtime groups, compositor, bindings, or scenarios.",
          "text": ""
        },
        {
          "id": "version",
          "anchor": "version",
          "heading": "Version provenance",
          "kind": "provenance",
          "required": true,
          "purpose": "Engine package version this page describes, when the API first appeared, and a warning if the live package.json has moved on.",
          "text": "Ships in @cyberart-io/engine 0.0.9. First appeared in 0.0.6."
        }
      ]
    },
    {
      "id": "visual-layers",
      "slug": "visual-layers",
      "route": "/docs/features/visual-layers",
      "title": "Visual layers",
      "purpose": "Versioned overlay layers, deterministic swaps, and hostState JSON projected onto paint.",
      "status": "substantial",
      "navGroup": "composition",
      "packageName": "@cyberart-io/engine",
      "enginePackageVersion": "0.0.12",
      "compatibleEngineVersions": {
        "min": "0.0.7"
      },
      "documentationSource": {
        "kind": "site-page",
        "path": "src/docs/pages/visual-layers.ts"
      },
      "exampleIds": [],
      "relatedFeatureIds": [
        "compositor",
        "presentation-bindings",
        "semantic-layers",
        "presentation-sequences"
      ],
      "prerequisiteFeatureIds": [],
      "aliases": [],
      "keywords": [
        "visual-layers",
        "createVisualLayerController",
        "captureVisualLayers"
      ],
      "surfaces": [
        "browser",
        "headless"
      ],
      "primaryExports": [
        "createVisualLayerController",
        "captureVisualLayers"
      ],
      "agentRoute": "/docs/agent/features/visual-layers.json",
      "summary": "Versioned overlay layers, deterministic swaps, and hostState JSON projected onto paint.",
      "owningModule": "./canvas/visualLayers",
      "lifecycle": {
        "status": "substantial",
        "introducedIn": "0.0.7",
        "compatibleEngineVersions": {
          "min": "0.0.7"
        },
        "normative": true,
        "experimental": false,
        "deprecated": false,
        "successorFeatureIds": []
      },
      "evidence": {
        "implementationPath": "packages/engine/src/canvas/visualLayers.ts",
        "tests": [
          "packages/engine/src/canvas/visualLayers.spec.ts",
          "packages/engine/src/canvas/cyb-66-visual-layers.repro.spec.ts"
        ],
        "engineGuide": "packages/engine/docs/visual-layers.md"
      },
      "redirectsFrom": [],
      "examples": [],
      "pageSections": [
        {
          "id": "problem",
          "anchor": "problem",
          "heading": "What this solves",
          "kind": "tutorial",
          "required": true,
          "purpose": "Name the problem the feature exists to solve.",
          "text": "Hosts need versioned overlay layers, deterministic swaps, and a JSON hostState projection onto paint without each cart forking a scene graph."
        },
        {
          "id": "when-to-use",
          "anchor": "when-to-use",
          "heading": "When to use it",
          "kind": "tutorial",
          "required": true,
          "purpose": "Say when this is the right tool, and when a related feature is a better fit.",
          "text": "createVisualLayerController / captureVisualLayers for overlay v1/v2 style swaps; compositor stacks carts. presentation-bindings project host state onto versions."
        },
        {
          "id": "conceptual-model",
          "anchor": "conceptual-model",
          "heading": "Conceptual model",
          "kind": "tutorial",
          "required": true,
          "purpose": "Objects, states, and how they relate. Diagrams optional; names must match the API.",
          "text": "Versioned overlay layers; deterministic swaps. hostState JSON projected onto paint; captureVisualLayers for inspect."
        },
        {
          "id": "lifecycle",
          "anchor": "lifecycle",
          "heading": "Lifecycle",
          "kind": "normative",
          "required": true,
          "purpose": "Create / attach / run / snapshot / restore / destroy, including which events fire.",
          "text": "createVisualLayerController → set versions / hostState → paint → capture → snapshot hostState with the envelope if the host persists it."
        },
        {
          "id": "cart-vs-host",
          "anchor": "cart-vs-host",
          "heading": "Cart versus host",
          "kind": "normative",
          "required": true,
          "purpose": "What the cart may declare or emit versus what the host/runtime must provide. Do not blur the boundary.",
          "text": "The host owns hostState and which version is live; the controller maps that onto paint. Carts should not invent a second version key."
        },
        {
          "id": "contract",
          "anchor": "contract",
          "heading": "Minimal contract",
          "kind": "normative",
          "required": true,
          "purpose": "Smallest API/configuration surface that a correct integration uses, with types and defaults.",
          "text": "```ts\nimport { createVisualLayerController, captureVisualLayers } from '@cyberart-io/engine';\n\nconst layers = createVisualLayerController({ /* versions */ });\n```\n\nPrimary exports: createVisualLayerController, captureVisualLayers."
        },
        {
          "id": "failure",
          "anchor": "failure",
          "heading": "Failure and fallback",
          "kind": "normative",
          "required": true,
          "purpose": "How the feature fails closed, what events/diagnostics look like, and what the host should do.",
          "text": "Unknown versions fail closed or stay on last-good as documented in the guide — they do not pick a random overlay. Capture is inspect, not a secret channel."
        },
        {
          "id": "accessibility",
          "anchor": "accessibility",
          "heading": "Accessibility",
          "kind": "normative",
          "required": true,
          "purpose": "Keyboard, focus, reduced motion, captions/non-audio alternatives, contrast, and anything the feature itself publishes (e.g. semantic regions).",
          "text": "Overlay swaps that change meaning need a nonvisual equivalent (caption, semantic focus). reduced-motion still records the version id."
        },
        {
          "id": "example",
          "anchor": "example",
          "heading": "Runnable example",
          "kind": "example",
          "required": true,
          "purpose": "Link canonical example IDs from the manifest. Embed and exact source come from the lock; this section must not paste a drifting snippet.",
          "text": "No canonical example yet. packages/engine/docs/visual-layers.md and packages/engine/src/canvas/cyb-66-visual-layers.repro.spec.ts are the runtime proof."
        },
        {
          "id": "expected-result",
          "anchor": "expected-result",
          "heading": "Expected result",
          "kind": "example",
          "required": true,
          "purpose": "What a reader should see, hear, or be able to do after the example runs.",
          "text": "Selecting version v2 paints v2; captureVisualLayers reports the live version. Invalid versions do not silently pick v1 unless that is the documented default."
        },
        {
          "id": "source",
          "anchor": "source",
          "heading": "Source, tests, and version",
          "kind": "provenance",
          "required": true,
          "purpose": "Links to implementationPath, tests, engine guide, and the pinned enginePackageVersion.",
          "text": "Implementation packages/engine/src/canvas/visualLayers.ts. Tests: packages/engine/src/canvas/visualLayers.spec.ts and packages/engine/src/canvas/cyb-66-visual-layers.repro.spec.ts. Guide: packages/engine/docs/visual-layers.md."
        },
        {
          "id": "related",
          "anchor": "related",
          "heading": "Related features",
          "kind": "tutorial",
          "required": true,
          "purpose": "Prerequisites first, then relatedFeatureIds. Use manifest ids, not free text names.",
          "text": "Related: compositor, presentation-bindings, semantic-layers, presentation-sequences. Prerequisites: none."
        },
        {
          "id": "advanced",
          "anchor": "advanced",
          "heading": "Advanced composition",
          "kind": "tutorial",
          "required": false,
          "purpose": "Optional: combining this feature with runtime groups, compositor, bindings, or scenarios.",
          "text": ""
        },
        {
          "id": "version",
          "anchor": "version",
          "heading": "Version provenance",
          "kind": "provenance",
          "required": true,
          "purpose": "Engine package version this page describes, when the API first appeared, and a warning if the live package.json has moved on.",
          "text": "Ships in @cyberart-io/engine 0.0.9. First appeared in 0.0.7."
        }
      ]
    },
    {
      "id": "semantic-layers",
      "slug": "semantic-layers",
      "route": "/docs/features/semantic-layers",
      "title": "Semantic layers",
      "purpose": "Named mask/polygon/rect/depth regions, host a11y names/roles, and hit-test policies.",
      "status": "substantial",
      "navGroup": "composition",
      "packageName": "@cyberart-io/engine",
      "enginePackageVersion": "0.0.12",
      "compatibleEngineVersions": {
        "min": "0.0.9"
      },
      "documentationSource": {
        "kind": "site-page",
        "path": "src/docs/pages/semantic-layers.ts"
      },
      "exampleIds": [],
      "relatedFeatureIds": [
        "normalized-geometry",
        "visual-layers",
        "presentation-bindings",
        "pointer"
      ],
      "prerequisiteFeatureIds": [],
      "aliases": [],
      "keywords": [
        "semantic-layers",
        "createSemanticLayerController"
      ],
      "surfaces": [
        "browser",
        "headless"
      ],
      "primaryExports": [
        "createSemanticLayerController"
      ],
      "agentRoute": "/docs/agent/features/semantic-layers.json",
      "summary": "Named mask/polygon/rect/depth regions, host a11y names/roles, and hit-test policies.",
      "owningModule": "./canvas/semanticLayers",
      "lifecycle": {
        "status": "substantial",
        "introducedIn": "0.0.9",
        "compatibleEngineVersions": {
          "min": "0.0.9"
        },
        "normative": true,
        "experimental": false,
        "deprecated": false,
        "successorFeatureIds": []
      },
      "evidence": {
        "implementationPath": "packages/engine/src/canvas/semanticLayers.ts",
        "tests": [
          "packages/engine/src/canvas/semanticLayers.spec.ts",
          "packages/engine/src/canvas/cyb-73-semantic-layers.repro.spec.ts"
        ],
        "engineGuide": "packages/engine/docs/semantic-layers.md"
      },
      "redirectsFrom": [],
      "examples": [],
      "pageSections": [
        {
          "id": "problem",
          "anchor": "problem",
          "heading": "What this solves",
          "kind": "tutorial",
          "required": true,
          "purpose": "Name the problem the feature exists to solve.",
          "text": "Canvas hit-testing needs named regions with host a11y names/roles, not only pixel color picks."
        },
        {
          "id": "when-to-use",
          "anchor": "when-to-use",
          "heading": "When to use it",
          "kind": "tutorial",
          "required": true,
          "purpose": "Say when this is the right tool, and when a related feature is a better fit.",
          "text": "createSemanticLayerController for mask/polygon/rect/depth regions; normalized-geometry supplies coordinates. presentation-bindings can drive presence; Pointer still delivers raw pixels unless you hit-test here."
        },
        {
          "id": "conceptual-model",
          "anchor": "conceptual-model",
          "heading": "Conceptual model",
          "kind": "tutorial",
          "required": true,
          "purpose": "Objects, states, and how they relate. Diagrams optional; names must match the API.",
          "text": "Named regions, host a11y names/roles, hit-test policies. Snapshot JSON may live in envelope hostState."
        },
        {
          "id": "lifecycle",
          "anchor": "lifecycle",
          "heading": "Lifecycle",
          "kind": "normative",
          "required": true,
          "purpose": "Create / attach / run / snapshot / restore / destroy, including which events fire.",
          "text": "createSemanticLayerController → define regions → hit-test / expose names to the host → snapshot/restore host-owned JSON."
        },
        {
          "id": "cart-vs-host",
          "anchor": "cart-vs-host",
          "heading": "Cart versus host",
          "kind": "normative",
          "required": true,
          "purpose": "What the cart may declare or emit versus what the host/runtime must provide. Do not blur the boundary.",
          "text": "The host supplies accessible names and roles and wires them to the document; the controller stores geometry and policies. Do not rely on canvas pixels for screen readers."
        },
        {
          "id": "contract",
          "anchor": "contract",
          "heading": "Minimal contract",
          "kind": "normative",
          "required": true,
          "purpose": "Smallest API/configuration surface that a correct integration uses, with types and defaults.",
          "text": "```ts\nimport { createSemanticLayerController } from '@cyberart-io/engine';\n\nconst semantics = createSemanticLayerController();\n```\n\nPrimary export: createSemanticLayerController."
        },
        {
          "id": "failure",
          "anchor": "failure",
          "heading": "Failure and fallback",
          "kind": "normative",
          "required": true,
          "purpose": "How the feature fails closed, what events/diagnostics look like, and what the host should do.",
          "text": "Unknown region ids fail hit-test closed (no hit), not a throw into the rAF loop. Restore must validate snapshot JSON before apply."
        },
        {
          "id": "accessibility",
          "anchor": "accessibility",
          "heading": "Accessibility",
          "kind": "normative",
          "required": true,
          "purpose": "Keyboard, focus, reduced motion, captions/non-audio alternatives, contrast, and anything the feature itself publishes (e.g. semantic regions).",
          "text": "This is the a11y surface for canvas regions: names, roles, hover/focus/selected as the host projects them. Always provide a non-canvas name for each interactive region."
        },
        {
          "id": "example",
          "anchor": "example",
          "heading": "Runnable example",
          "kind": "example",
          "required": true,
          "purpose": "Link canonical example IDs from the manifest. Embed and exact source come from the lock; this section must not paste a drifting snippet.",
          "text": "No canonical example yet. packages/engine/docs/semantic-layers.md and packages/engine/src/canvas/cyb-73-semantic-layers.repro.spec.ts are the runtime proof."
        },
        {
          "id": "expected-result",
          "anchor": "expected-result",
          "heading": "Expected result",
          "kind": "example",
          "required": true,
          "purpose": "What a reader should see, hear, or be able to do after the example runs.",
          "text": "Hit-testing a named rect returns that region; the host can read its name/role. Pixels outside all regions miss."
        },
        {
          "id": "source",
          "anchor": "source",
          "heading": "Source, tests, and version",
          "kind": "provenance",
          "required": true,
          "purpose": "Links to implementationPath, tests, engine guide, and the pinned enginePackageVersion.",
          "text": "Implementation packages/engine/src/canvas/semanticLayers.ts. Tests: packages/engine/src/canvas/semanticLayers.spec.ts and packages/engine/src/canvas/cyb-73-semantic-layers.repro.spec.ts. Guide: packages/engine/docs/semantic-layers.md."
        },
        {
          "id": "related",
          "anchor": "related",
          "heading": "Related features",
          "kind": "tutorial",
          "required": true,
          "purpose": "Prerequisites first, then relatedFeatureIds. Use manifest ids, not free text names.",
          "text": "Related: normalized-geometry, visual-layers, presentation-bindings, pointer. Prerequisites: none."
        },
        {
          "id": "advanced",
          "anchor": "advanced",
          "heading": "Advanced composition",
          "kind": "tutorial",
          "required": false,
          "purpose": "Optional: combining this feature with runtime groups, compositor, bindings, or scenarios.",
          "text": ""
        },
        {
          "id": "version",
          "anchor": "version",
          "heading": "Version provenance",
          "kind": "provenance",
          "required": true,
          "purpose": "Engine package version this page describes, when the API first appeared, and a warning if the live package.json has moved on.",
          "text": "Ships in @cyberart-io/engine 0.0.9. First appeared in 0.0.9."
        }
      ]
    },
    {
      "id": "presentation-sequences",
      "slug": "presentation-sequences",
      "route": "/docs/features/presentation-sequences",
      "title": "Presentation sequences",
      "purpose": "One frame-stepped clock across audio, captions, semantic focus, visual layers, and cues, with skip/fallback/restore.",
      "status": "substantial",
      "navGroup": "events-presentation",
      "packageName": "@cyberart-io/engine",
      "enginePackageVersion": "0.0.12",
      "compatibleEngineVersions": {
        "min": "0.0.9"
      },
      "documentationSource": {
        "kind": "site-page",
        "path": "src/docs/pages/presentation-sequences.ts"
      },
      "exampleIds": [],
      "relatedFeatureIds": [
        "presentation-cue",
        "audio",
        "visual-layers",
        "semantic-layers",
        "presentation-bindings"
      ],
      "prerequisiteFeatureIds": [],
      "aliases": [],
      "keywords": [
        "presentation-sequences",
        "definePresentationSequence",
        "createPresentationSequencePlayer"
      ],
      "surfaces": [
        "browser",
        "headless"
      ],
      "primaryExports": [
        "definePresentationSequence",
        "createPresentationSequencePlayer"
      ],
      "agentRoute": "/docs/agent/features/presentation-sequences.json",
      "summary": "One frame-stepped clock across audio, captions, semantic focus, visual layers, and cues, with skip/fallback/restore.",
      "owningModule": "./canvas/presentationSequence",
      "lifecycle": {
        "status": "substantial",
        "introducedIn": "0.0.9",
        "compatibleEngineVersions": {
          "min": "0.0.9"
        },
        "normative": true,
        "experimental": false,
        "deprecated": false,
        "successorFeatureIds": []
      },
      "evidence": {
        "implementationPath": "packages/engine/src/canvas/presentationSequence.ts",
        "tests": [
          "packages/engine/src/canvas/presentationSequence.spec.ts",
          "packages/engine/src/canvas/cyb-78-presentation-sequence.repro.spec.ts"
        ],
        "engineGuide": "packages/engine/docs/presentation-sequences.md"
      },
      "redirectsFrom": [],
      "examples": [],
      "pageSections": [
        {
          "id": "problem",
          "anchor": "problem",
          "heading": "What this solves",
          "kind": "tutorial",
          "required": true,
          "purpose": "Name the problem the feature exists to solve.",
          "text": "Audio, captions, semantic focus, visual layers, and cues need one clock with skip, fallback, and restore — not five timers."
        },
        {
          "id": "when-to-use",
          "anchor": "when-to-use",
          "heading": "When to use it",
          "kind": "tutorial",
          "required": true,
          "purpose": "Say when this is the right tool, and when a related feature is a better fit.",
          "text": "definePresentationSequence / createPresentationSequencePlayer when those tracks must stay aligned. A single presentation-cue timeline is enough for one track."
        },
        {
          "id": "conceptual-model",
          "anchor": "conceptual-model",
          "heading": "Conceptual model",
          "kind": "tutorial",
          "required": true,
          "purpose": "Objects, states, and how they relate. Diagrams optional; names must match the API.",
          "text": "One frame-stepped clock across audio, captions, semantic focus, visual layers, and cues. skip / fallback / restore are first-class."
        },
        {
          "id": "lifecycle",
          "anchor": "lifecycle",
          "heading": "Lifecycle",
          "kind": "normative",
          "required": true,
          "purpose": "Create / attach / run / snapshot / restore / destroy, including which events fire.",
          "text": "definePresentationSequence → createPresentationSequencePlayer → step → skip/fallback/restore → destroy."
        },
        {
          "id": "cart-vs-host",
          "anchor": "cart-vs-host",
          "heading": "Cart versus host",
          "kind": "normative",
          "required": true,
          "purpose": "What the cart may declare or emit versus what the host/runtime must provide. Do not blur the boundary.",
          "text": "The host owns the player and skip/fallback policy; tracks are data. Carts should not start competing rAF loops for captions."
        },
        {
          "id": "contract",
          "anchor": "contract",
          "heading": "Minimal contract",
          "kind": "normative",
          "required": true,
          "purpose": "Smallest API/configuration surface that a correct integration uses, with types and defaults.",
          "text": "```ts\nimport { definePresentationSequence, createPresentationSequencePlayer } from '@cyberart-io/engine';\n\nconst defined = definePresentationSequence({ /* tracks */ });\nconst player = createPresentationSequencePlayer(defined.sequence);\n```\n\nPrimary exports: definePresentationSequence, createPresentationSequencePlayer."
        },
        {
          "id": "failure",
          "anchor": "failure",
          "heading": "Failure and fallback",
          "kind": "normative",
          "required": true,
          "purpose": "How the feature fails closed, what events/diagnostics look like, and what the host should do.",
          "text": "Fallback is explicit; restore must re-enter a valid clock position. Do not treat skip as delete; fail closed on invalid sequence documents."
        },
        {
          "id": "accessibility",
          "anchor": "accessibility",
          "heading": "Accessibility",
          "kind": "normative",
          "required": true,
          "purpose": "Keyboard, focus, reduced motion, captions/non-audio alternatives, contrast, and anything the feature itself publishes (e.g. semantic regions).",
          "text": "Captions and semantic focus are required tracks when audio or visual meaning changes. reduced-motion/sensory still step the clock so restore works."
        },
        {
          "id": "example",
          "anchor": "example",
          "heading": "Runnable example",
          "kind": "example",
          "required": true,
          "purpose": "Link canonical example IDs from the manifest. Embed and exact source come from the lock; this section must not paste a drifting snippet.",
          "text": "No canonical example yet. packages/engine/docs/presentation-sequences.md and packages/engine/src/canvas/cyb-78-presentation-sequence.repro.spec.ts are the runtime proof."
        },
        {
          "id": "expected-result",
          "anchor": "expected-result",
          "heading": "Expected result",
          "kind": "example",
          "required": true,
          "purpose": "What a reader should see, hear, or be able to do after the example runs.",
          "text": "Stepping the player advances every track to the same frame. skip/fallback/restore leave inspectable positions."
        },
        {
          "id": "source",
          "anchor": "source",
          "heading": "Source, tests, and version",
          "kind": "provenance",
          "required": true,
          "purpose": "Links to implementationPath, tests, engine guide, and the pinned enginePackageVersion.",
          "text": "Implementation packages/engine/src/canvas/presentationSequence.ts. Tests: packages/engine/src/canvas/presentationSequence.spec.ts and packages/engine/src/canvas/cyb-78-presentation-sequence.repro.spec.ts. Guide: packages/engine/docs/presentation-sequences.md."
        },
        {
          "id": "related",
          "anchor": "related",
          "heading": "Related features",
          "kind": "tutorial",
          "required": true,
          "purpose": "Prerequisites first, then relatedFeatureIds. Use manifest ids, not free text names.",
          "text": "Related: presentation-cue, audio, visual-layers, semantic-layers, presentation-bindings. Prerequisites: none."
        },
        {
          "id": "advanced",
          "anchor": "advanced",
          "heading": "Advanced composition",
          "kind": "tutorial",
          "required": false,
          "purpose": "Optional: combining this feature with runtime groups, compositor, bindings, or scenarios.",
          "text": ""
        },
        {
          "id": "version",
          "anchor": "version",
          "heading": "Version provenance",
          "kind": "provenance",
          "required": true,
          "purpose": "Engine package version this page describes, when the API first appeared, and a warning if the live package.json has moved on.",
          "text": "Ships in @cyberart-io/engine 0.0.9. First appeared in 0.0.9."
        }
      ]
    },
    {
      "id": "presentation-bindings",
      "slug": "presentation-bindings",
      "route": "/docs/features/presentation-bindings",
      "title": "Presentation bindings",
      "purpose": "Versioned manifest projecting host state onto visual versions, presence, regions, and sequences; fail-closed defaults.",
      "status": "substantial",
      "navGroup": "events-presentation",
      "packageName": "@cyberart-io/engine",
      "enginePackageVersion": "0.0.12",
      "compatibleEngineVersions": {
        "min": "0.0.9"
      },
      "documentationSource": {
        "kind": "site-page",
        "path": "src/docs/pages/presentation-bindings.ts"
      },
      "exampleIds": [],
      "relatedFeatureIds": [
        "visual-layers",
        "semantic-layers",
        "presentation-sequences",
        "selection-trace"
      ],
      "prerequisiteFeatureIds": [],
      "aliases": [],
      "keywords": [
        "presentation-bindings",
        "definePresentationBindings",
        "createPresentationBindingRuntime"
      ],
      "surfaces": [
        "browser",
        "headless"
      ],
      "primaryExports": [
        "definePresentationBindings",
        "createPresentationBindingRuntime"
      ],
      "agentRoute": "/docs/agent/features/presentation-bindings.json",
      "summary": "Versioned manifest projecting host state onto visual versions, presence, regions, and sequences; fail-closed defaults.",
      "owningModule": "./canvas/presentationBindings",
      "lifecycle": {
        "status": "substantial",
        "introducedIn": "0.0.9",
        "compatibleEngineVersions": {
          "min": "0.0.9"
        },
        "normative": true,
        "experimental": false,
        "deprecated": false,
        "successorFeatureIds": []
      },
      "evidence": {
        "implementationPath": "packages/engine/src/canvas/presentationBindings.ts",
        "tests": [
          "packages/engine/src/canvas/presentationBindings.spec.ts",
          "packages/engine/src/canvas/cyb-83-presentation-bindings.repro.spec.ts"
        ],
        "engineGuide": "packages/engine/docs/presentation-bindings.md"
      },
      "redirectsFrom": [],
      "examples": [],
      "pageSections": [
        {
          "id": "problem",
          "anchor": "problem",
          "heading": "What this solves",
          "kind": "tutorial",
          "required": true,
          "purpose": "Name the problem the feature exists to solve.",
          "text": "Host state needs a versioned projection onto visual versions, presence, regions, and sequences without silent defaulting to a pretty overlay."
        },
        {
          "id": "when-to-use",
          "anchor": "when-to-use",
          "heading": "When to use it",
          "kind": "tutorial",
          "required": true,
          "purpose": "Say when this is the right tool, and when a related feature is a better fit.",
          "text": "definePresentationBindings / createPresentationBindingRuntime when host JSON drives presentation; selection-trace records why a binding won. visual-layers/semantic-layers/sequences are the targets."
        },
        {
          "id": "conceptual-model",
          "anchor": "conceptual-model",
          "heading": "Conceptual model",
          "kind": "tutorial",
          "required": true,
          "purpose": "Objects, states, and how they relate. Diagrams optional; names must match the API.",
          "text": "Versioned manifest; host state → visual versions, presence, regions, sequences. Fail-closed defaults; snapshot JSON in hostState."
        },
        {
          "id": "lifecycle",
          "anchor": "lifecycle",
          "heading": "Lifecycle",
          "kind": "normative",
          "required": true,
          "purpose": "Create / attach / run / snapshot / restore / destroy, including which events fire.",
          "text": "definePresentationBindings → createPresentationBindingRuntime → push host state → inspect selection → snapshot/restore."
        },
        {
          "id": "cart-vs-host",
          "anchor": "cart-vs-host",
          "heading": "Cart versus host",
          "kind": "normative",
          "required": true,
          "purpose": "What the cart may declare or emit versus what the host/runtime must provide. Do not blur the boundary.",
          "text": "The host owns host state; bindings are data. The runtime fails closed when a field is missing rather than inventing a look."
        },
        {
          "id": "contract",
          "anchor": "contract",
          "heading": "Minimal contract",
          "kind": "normative",
          "required": true,
          "purpose": "Smallest API/configuration surface that a correct integration uses, with types and defaults.",
          "text": "```ts\nimport { definePresentationBindings, createPresentationBindingRuntime } from '@cyberart-io/engine';\n\nconst defined = definePresentationBindings({ /* manifest */ });\nconst runtime = createPresentationBindingRuntime(defined.bindings);\n```\n\nPrimary exports: definePresentationBindings, createPresentationBindingRuntime."
        },
        {
          "id": "failure",
          "anchor": "failure",
          "heading": "Failure and fallback",
          "kind": "normative",
          "required": true,
          "purpose": "How the feature fails closed, what events/diagnostics look like, and what the host should do.",
          "text": "Missing mappings fail closed; Do not ship a “best effort” overlay. selection-trace should record the reason, including fallback."
        },
        {
          "id": "accessibility",
          "anchor": "accessibility",
          "heading": "Accessibility",
          "kind": "normative",
          "required": true,
          "purpose": "Keyboard, focus, reduced motion, captions/non-audio alternatives, contrast, and anything the feature itself publishes (e.g. semantic regions).",
          "text": "Presence and region bindings must keep names/roles. A failed binding is a text error, not an unlabeled canvas."
        },
        {
          "id": "example",
          "anchor": "example",
          "heading": "Runnable example",
          "kind": "example",
          "required": true,
          "purpose": "Link canonical example IDs from the manifest. Embed and exact source come from the lock; this section must not paste a drifting snippet.",
          "text": "No canonical example yet. packages/engine/docs/presentation-bindings.md and packages/engine/src/canvas/cyb-83-presentation-bindings.repro.spec.ts are the runtime proof."
        },
        {
          "id": "expected-result",
          "anchor": "expected-result",
          "heading": "Expected result",
          "kind": "example",
          "required": true,
          "purpose": "What a reader should see, hear, or be able to do after the example runs.",
          "text": "Valid host state projects onto the declared visual version. Invalid state does not apply a silent default look."
        },
        {
          "id": "source",
          "anchor": "source",
          "heading": "Source, tests, and version",
          "kind": "provenance",
          "required": true,
          "purpose": "Links to implementationPath, tests, engine guide, and the pinned enginePackageVersion.",
          "text": "Implementation packages/engine/src/canvas/presentationBindings.ts. Tests: packages/engine/src/canvas/presentationBindings.spec.ts and packages/engine/src/canvas/cyb-83-presentation-bindings.repro.spec.ts. Guide: packages/engine/docs/presentation-bindings.md."
        },
        {
          "id": "related",
          "anchor": "related",
          "heading": "Related features",
          "kind": "tutorial",
          "required": true,
          "purpose": "Prerequisites first, then relatedFeatureIds. Use manifest ids, not free text names.",
          "text": "Related: visual-layers, semantic-layers, presentation-sequences, selection-trace. Prerequisites: none."
        },
        {
          "id": "advanced",
          "anchor": "advanced",
          "heading": "Advanced composition",
          "kind": "tutorial",
          "required": false,
          "purpose": "Optional: combining this feature with runtime groups, compositor, bindings, or scenarios.",
          "text": ""
        },
        {
          "id": "version",
          "anchor": "version",
          "heading": "Version provenance",
          "kind": "provenance",
          "required": true,
          "purpose": "Engine package version this page describes, when the API first appeared, and a warning if the live package.json has moved on.",
          "text": "Ships in @cyberart-io/engine 0.0.9. First appeared in 0.0.9."
        }
      ]
    },
    {
      "id": "portal-lifecycle",
      "slug": "portal-lifecycle",
      "route": "/docs/features/portal-lifecycle",
      "title": "Portal lifecycle",
      "purpose": "Enter/exit/abort another cart exclusively, with parent suspend, grant transfer, and nested stack restore.",
      "status": "substantial",
      "navGroup": "composition",
      "packageName": "@cyberart-io/engine",
      "enginePackageVersion": "0.0.12",
      "compatibleEngineVersions": {
        "min": "0.0.9"
      },
      "documentationSource": {
        "kind": "site-page",
        "path": "src/docs/pages/portal-lifecycle.ts"
      },
      "exampleIds": [],
      "relatedFeatureIds": [
        "runtime-group",
        "remote-cart-manifest"
      ],
      "prerequisiteFeatureIds": [
        "runtime-group"
      ],
      "aliases": [],
      "keywords": [
        "portal-lifecycle",
        "createPortalLifecycle",
        "cabinetPortal",
        "paintingPortal"
      ],
      "surfaces": [
        "browser",
        "headless"
      ],
      "primaryExports": [
        "createPortalLifecycle",
        "cabinetPortal",
        "paintingPortal"
      ],
      "agentRoute": "/docs/agent/features/portal-lifecycle.json",
      "summary": "Enter/exit/abort another cart exclusively, with parent suspend, grant transfer, and nested stack restore.",
      "owningModule": "./canvas/portalLifecycle",
      "lifecycle": {
        "status": "substantial",
        "introducedIn": "0.0.9",
        "compatibleEngineVersions": {
          "min": "0.0.9"
        },
        "normative": true,
        "experimental": false,
        "deprecated": false,
        "successorFeatureIds": []
      },
      "evidence": {
        "implementationPath": "packages/engine/src/canvas/portalLifecycle.ts",
        "tests": [
          "packages/engine/src/canvas/portalLifecycle.spec.ts",
          "packages/engine/src/canvas/cyb-75-portal-lifecycle.repro.spec.ts"
        ],
        "engineGuide": "packages/engine/docs/portal-lifecycle.md"
      },
      "redirectsFrom": [],
      "examples": [],
      "pageSections": [
        {
          "id": "problem",
          "anchor": "problem",
          "heading": "What this solves",
          "kind": "tutorial",
          "required": true,
          "purpose": "Name the problem the feature exists to solve.",
          "text": "Entering another cart exclusively needs parent suspend, grant transfer, and nested stack restore — not two runtimes fighting over one container."
        },
        {
          "id": "when-to-use",
          "anchor": "when-to-use",
          "heading": "When to use it",
          "kind": "tutorial",
          "required": true,
          "purpose": "Say when this is the right tool, and when a related feature is a better fit.",
          "text": "createPortalLifecycle / cabinetPortal / paintingPortal when a cart yields the surface to another. runtime-group is shared simultaneous carts, not exclusive enter."
        },
        {
          "id": "conceptual-model",
          "anchor": "conceptual-model",
          "heading": "Conceptual model",
          "kind": "tutorial",
          "required": true,
          "purpose": "Objects, states, and how they relate. Diagrams optional; names must match the API.",
          "text": "Enter/exit/abort another cart exclusively; parent suspend. Grant transfer; nested stack restore. Snapshot JSON in hostState."
        },
        {
          "id": "lifecycle",
          "anchor": "lifecycle",
          "heading": "Lifecycle",
          "kind": "normative",
          "required": true,
          "purpose": "Create / attach / run / snapshot / restore / destroy, including which events fire.",
          "text": "createPortalLifecycle → enter(target) → child runs → exit or abort → parent restore. Nested enters push; abort unwinds as documented."
        },
        {
          "id": "cart-vs-host",
          "anchor": "cart-vs-host",
          "heading": "Cart versus host",
          "kind": "normative",
          "required": true,
          "purpose": "What the cart may declare or emit versus what the host/runtime must provide. Do not blur the boundary.",
          "text": "The host owns grants and which cart is exclusive; carts request enter; they do not steal the container. Audio teardown follows the participant id."
        },
        {
          "id": "contract",
          "anchor": "contract",
          "heading": "Minimal contract",
          "kind": "normative",
          "required": true,
          "purpose": "Smallest API/configuration surface that a correct integration uses, with types and defaults.",
          "text": "```ts\nimport { createPortalLifecycle, cabinetPortal, paintingPortal } from '@cyberart-io/engine';\n\nconst portals = createPortalLifecycle({ /* stack */ });\n```\n\nPrimary exports: createPortalLifecycle, cabinetPortal, paintingPortal."
        },
        {
          "id": "failure",
          "anchor": "failure",
          "heading": "Failure and fallback",
          "kind": "normative",
          "required": true,
          "purpose": "How the feature fails closed, what events/diagnostics look like, and what the host should do.",
          "text": "Abort vs exit are distinct; failed enter must not leave grants on the child. Restore must re-attach the parent; fail closed on unknown targets."
        },
        {
          "id": "accessibility",
          "anchor": "accessibility",
          "heading": "Accessibility",
          "kind": "normative",
          "required": true,
          "purpose": "Keyboard, focus, reduced motion, captions/non-audio alternatives, contrast, and anything the feature itself publishes (e.g. semantic regions).",
          "text": "Announce which cart is exclusive; restore focus to the parent’s host controls. Nested stacks need a way out that is not canvas-only."
        },
        {
          "id": "example",
          "anchor": "example",
          "heading": "Runnable example",
          "kind": "example",
          "required": true,
          "purpose": "Link canonical example IDs from the manifest. Embed and exact source come from the lock; this section must not paste a drifting snippet.",
          "text": "No canonical example yet. packages/engine/docs/portal-lifecycle.md and packages/engine/src/canvas/cyb-75-portal-lifecycle.repro.spec.ts are the runtime proof."
        },
        {
          "id": "expected-result",
          "anchor": "expected-result",
          "heading": "Expected result",
          "kind": "example",
          "required": true,
          "purpose": "What a reader should see, hear, or be able to do after the example runs.",
          "text": "enter suspends the parent and runs the child; exit restores the parent and grants. abort does not leave a dangling child."
        },
        {
          "id": "source",
          "anchor": "source",
          "heading": "Source, tests, and version",
          "kind": "provenance",
          "required": true,
          "purpose": "Links to implementationPath, tests, engine guide, and the pinned enginePackageVersion.",
          "text": "Implementation packages/engine/src/canvas/portalLifecycle.ts. Tests: packages/engine/src/canvas/portalLifecycle.spec.ts and packages/engine/src/canvas/cyb-75-portal-lifecycle.repro.spec.ts. Guide: packages/engine/docs/portal-lifecycle.md."
        },
        {
          "id": "related",
          "anchor": "related",
          "heading": "Related features",
          "kind": "tutorial",
          "required": true,
          "purpose": "Prerequisites first, then relatedFeatureIds. Use manifest ids, not free text names.",
          "text": "Prerequisite: runtime-group. Related: remote-cart-manifest."
        },
        {
          "id": "advanced",
          "anchor": "advanced",
          "heading": "Advanced composition",
          "kind": "tutorial",
          "required": false,
          "purpose": "Optional: combining this feature with runtime groups, compositor, bindings, or scenarios.",
          "text": ""
        },
        {
          "id": "version",
          "anchor": "version",
          "heading": "Version provenance",
          "kind": "provenance",
          "required": true,
          "purpose": "Engine package version this page describes, when the API first appeared, and a warning if the live package.json has moved on.",
          "text": "Ships in @cyberart-io/engine 0.0.9. First appeared in 0.0.9."
        }
      ]
    },
    {
      "id": "job-orchestration",
      "slug": "job-orchestration",
      "route": "/docs/features/job-orchestration",
      "title": "Job orchestration",
      "purpose": "Durable queued jobs with retries, leases, evaluator feedback, and host-owned apply. Results are refs, not payloads.",
      "status": "substantial",
      "navGroup": "world-content",
      "packageName": "@cyberart-io/engine",
      "enginePackageVersion": "0.0.12",
      "compatibleEngineVersions": {
        "min": "0.0.9"
      },
      "documentationSource": {
        "kind": "site-page",
        "path": "src/docs/pages/job-orchestration.ts"
      },
      "exampleIds": [],
      "relatedFeatureIds": [
        "world-patch",
        "content-revision",
        "headless-harness"
      ],
      "prerequisiteFeatureIds": [],
      "aliases": [],
      "keywords": [
        "job-orchestration",
        "createJobCoordinator",
        "createMemoryJobPersistence",
        "createHeadlessJobWorker"
      ],
      "surfaces": [
        "browser",
        "headless"
      ],
      "primaryExports": [
        "createJobCoordinator",
        "createMemoryJobPersistence",
        "createHeadlessJobWorker"
      ],
      "agentRoute": "/docs/agent/features/job-orchestration.json",
      "summary": "Durable queued jobs with retries, leases, evaluator feedback, and host-owned apply. Results are refs, not payloads.",
      "owningModule": "./canvas/jobOrchestration",
      "lifecycle": {
        "status": "substantial",
        "introducedIn": "0.0.9",
        "compatibleEngineVersions": {
          "min": "0.0.9"
        },
        "normative": true,
        "experimental": false,
        "deprecated": false,
        "successorFeatureIds": []
      },
      "evidence": {
        "implementationPath": "packages/engine/src/canvas/jobOrchestration.ts",
        "tests": [
          "packages/engine/src/canvas/jobOrchestration.spec.ts",
          "packages/engine/src/canvas/cyb-80-job-orchestration.repro.spec.ts",
          "packages/engine/src/canvas/cyb-112-job-payload-validation.repro.spec.ts"
        ],
        "engineGuide": "packages/engine/docs/job-orchestration.md"
      },
      "redirectsFrom": [],
      "examples": [],
      "pageSections": [
        {
          "id": "problem",
          "anchor": "problem",
          "heading": "What this solves",
          "kind": "tutorial",
          "required": true,
          "purpose": "Name the problem the feature exists to solve.",
          "text": "Durable queued work needs retries, leases, evaluator feedback, and host-owned apply, without putting result payloads in the event log."
        },
        {
          "id": "when-to-use",
          "anchor": "when-to-use",
          "heading": "When to use it",
          "kind": "tutorial",
          "required": true,
          "purpose": "Say when this is the right tool, and when a related feature is a better fit.",
          "text": "createJobCoordinator / createMemoryJobPersistence / createHeadlessJobWorker for generated work; world-patch and content-revision consume results. Do not block the rAF loop on a job."
        },
        {
          "id": "conceptual-model",
          "anchor": "conceptual-model",
          "heading": "Conceptual model",
          "kind": "tutorial",
          "required": true,
          "purpose": "Objects, states, and how they relate. Diagrams optional; names must match the API.",
          "text": "Durable queued jobs, retries, leases, evaluator feedback; results are refs, not payloads. Host-owned apply; Headless fake worker for CI."
        },
        {
          "id": "lifecycle",
          "anchor": "lifecycle",
          "heading": "Lifecycle",
          "kind": "normative",
          "required": true,
          "purpose": "Create / attach / run / snapshot / restore / destroy, including which events fire.",
          "text": "createJobCoordinator → enqueue → worker lease → complete with ref → host apply → persist. Retry on failure within lease rules."
        },
        {
          "id": "cart-vs-host",
          "anchor": "cart-vs-host",
          "heading": "Cart versus host",
          "kind": "normative",
          "required": true,
          "purpose": "What the cart may declare or emit versus what the host/runtime must provide. Do not blur the boundary.",
          "text": "The host owns persistence and apply; workers are host or headless fakes. Carts enqueue; they do not write the world themselves."
        },
        {
          "id": "contract",
          "anchor": "contract",
          "heading": "Minimal contract",
          "kind": "normative",
          "required": true,
          "purpose": "Smallest API/configuration surface that a correct integration uses, with types and defaults.",
          "text": "```ts\nimport { createJobCoordinator, createMemoryJobPersistence, createHeadlessJobWorker } from '@cyberart-io/engine';\n\nconst jobs = createJobCoordinator({ persistence: createMemoryJobPersistence() });\n```\n\nPrimary exports: createJobCoordinator, createMemoryJobPersistence, createHeadlessJobWorker."
        },
        {
          "id": "failure",
          "anchor": "failure",
          "heading": "Failure and fallback",
          "kind": "normative",
          "required": true,
          "purpose": "How the feature fails closed, what events/diagnostics look like, and what the host should do.",
          "text": "Lease expiry and retries are explicit; A failed apply must not mark the job done. Results-as-refs means a missing blob is a host error, not a silent empty world."
        },
        {
          "id": "accessibility",
          "anchor": "accessibility",
          "heading": "Accessibility",
          "kind": "normative",
          "required": true,
          "purpose": "Keyboard, focus, reduced motion, captions/non-audio alternatives, contrast, and anything the feature itself publishes (e.g. semantic regions).",
          "text": "Job status belongs in host UI text. Do not use canvas color as the only in-progress signal."
        },
        {
          "id": "example",
          "anchor": "example",
          "heading": "Runnable example",
          "kind": "example",
          "required": true,
          "purpose": "Link canonical example IDs from the manifest. Embed and exact source come from the lock; this section must not paste a drifting snippet.",
          "text": "No canonical example yet. packages/engine/docs/job-orchestration.md, packages/engine/src/canvas/cyb-80-job-orchestration.repro.spec.ts, and packages/engine/src/canvas/cyb-112-job-payload-validation.repro.spec.ts are the runtime proof."
        },
        {
          "id": "expected-result",
          "anchor": "expected-result",
          "heading": "Expected result",
          "kind": "example",
          "required": true,
          "purpose": "What a reader should see, hear, or be able to do after the example runs.",
          "text": "A queued job completes with a ref; the host apply is a separate step. Headless workers do not need a network."
        },
        {
          "id": "source",
          "anchor": "source",
          "heading": "Source, tests, and version",
          "kind": "provenance",
          "required": true,
          "purpose": "Links to implementationPath, tests, engine guide, and the pinned enginePackageVersion.",
          "text": "Implementation packages/engine/src/canvas/jobOrchestration.ts. Tests: packages/engine/src/canvas/jobOrchestration.spec.ts, packages/engine/src/canvas/cyb-80-job-orchestration.repro.spec.ts, and packages/engine/src/canvas/cyb-112-job-payload-validation.repro.spec.ts. Guide: packages/engine/docs/job-orchestration.md."
        },
        {
          "id": "related",
          "anchor": "related",
          "heading": "Related features",
          "kind": "tutorial",
          "required": true,
          "purpose": "Prerequisites first, then relatedFeatureIds. Use manifest ids, not free text names.",
          "text": "Related: world-patch, content-revision, headless-harness. Prerequisites: none."
        },
        {
          "id": "advanced",
          "anchor": "advanced",
          "heading": "Advanced composition",
          "kind": "tutorial",
          "required": false,
          "purpose": "Optional: combining this feature with runtime groups, compositor, bindings, or scenarios.",
          "text": ""
        },
        {
          "id": "version",
          "anchor": "version",
          "heading": "Version provenance",
          "kind": "provenance",
          "required": true,
          "purpose": "Engine package version this page describes, when the API first appeared, and a warning if the live package.json has moved on.",
          "text": "Ships in @cyberart-io/engine 0.0.9. First appeared in 0.0.9."
        }
      ]
    },
    {
      "id": "world-graph",
      "slug": "world-graph",
      "route": "/docs/features/world-graph",
      "title": "World graph",
      "purpose": "Optional spatial graph with stable ids, canonical vs known projections, and semantic-time transit.",
      "status": "substantial",
      "navGroup": "world-content",
      "packageName": "@cyberart-io/engine",
      "enginePackageVersion": "0.0.12",
      "compatibleEngineVersions": {
        "min": "0.0.9"
      },
      "documentationSource": {
        "kind": "site-page",
        "path": "src/docs/pages/world-graph.ts"
      },
      "exampleIds": [],
      "relatedFeatureIds": [
        "world-patch",
        "semantic-layers"
      ],
      "prerequisiteFeatureIds": [],
      "aliases": [],
      "keywords": [
        "world-graph",
        "createWorldGraph",
        "worldGraphEventContracts"
      ],
      "surfaces": [
        "browser",
        "headless"
      ],
      "primaryExports": [
        "createWorldGraph",
        "worldGraphEventContracts"
      ],
      "agentRoute": "/docs/agent/features/world-graph.json",
      "summary": "Optional spatial graph with stable ids, canonical vs known projections, and semantic-time transit.",
      "owningModule": "./canvas/worldGraph",
      "lifecycle": {
        "status": "substantial",
        "introducedIn": "0.0.9",
        "compatibleEngineVersions": {
          "min": "0.0.9"
        },
        "normative": true,
        "experimental": false,
        "deprecated": false,
        "successorFeatureIds": []
      },
      "evidence": {
        "implementationPath": "packages/engine/src/canvas/worldGraph.ts",
        "tests": [
          "packages/engine/src/canvas/worldGraph.spec.ts",
          "packages/engine/src/canvas/cyb-81-world-graph.repro.spec.ts"
        ],
        "engineGuide": "packages/engine/docs/world-graph.md"
      },
      "redirectsFrom": [],
      "examples": [],
      "pageSections": [
        {
          "id": "problem",
          "anchor": "problem",
          "heading": "What this solves",
          "kind": "tutorial",
          "required": true,
          "purpose": "Name the problem the feature exists to solve.",
          "text": "Optional spatial graphs need stable ids, canonical vs known projections, and semantic-time transit — not a hidden global scene."
        },
        {
          "id": "when-to-use",
          "anchor": "when-to-use",
          "heading": "When to use it",
          "kind": "tutorial",
          "required": true,
          "purpose": "Say when this is the right tool, and when a related feature is a better fit.",
          "text": "createWorldGraph / worldGraphEventContracts when the host has a graph; world-patch applies generated edits. Skip this if the cart is a single canvas with no graph."
        },
        {
          "id": "conceptual-model",
          "anchor": "conceptual-model",
          "heading": "Conceptual model",
          "kind": "tutorial",
          "required": true,
          "purpose": "Objects, states, and how they relate. Diagrams optional; names must match the API.",
          "text": "Stable ids; Canonical vs known projections. Semantic-time transit; event contracts for graph changes."
        },
        {
          "id": "lifecycle",
          "anchor": "lifecycle",
          "heading": "Lifecycle",
          "kind": "normative",
          "required": true,
          "purpose": "Create / attach / run / snapshot / restore / destroy, including which events fire.",
          "text": "createWorldGraph → load projections → transit → snapshot hostState → destroy. Patches apply through world-patch, not ad-hoc mutation."
        },
        {
          "id": "cart-vs-host",
          "anchor": "cart-vs-host",
          "heading": "Cart versus host",
          "kind": "normative",
          "required": true,
          "purpose": "What the cart may declare or emit versus what the host/runtime must provide. Do not blur the boundary.",
          "text": "The host owns persistence and path policy; the graph is optional. Carts emit contracts; they do not hold the database."
        },
        {
          "id": "contract",
          "anchor": "contract",
          "heading": "Minimal contract",
          "kind": "normative",
          "required": true,
          "purpose": "Smallest API/configuration surface that a correct integration uses, with types and defaults.",
          "text": "```ts\nimport { createWorldGraph, worldGraphEventContracts } from '@cyberart-io/engine';\n\nconst graph = createWorldGraph();\n```\n\nPrimary exports: createWorldGraph, worldGraphEventContracts."
        },
        {
          "id": "failure",
          "anchor": "failure",
          "heading": "Failure and fallback",
          "kind": "normative",
          "required": true,
          "purpose": "How the feature fails closed, what events/diagnostics look like, and what the host should do.",
          "text": "Unknown ids fail closed; known vs canonical disagreement is data, not a crash, until a patch commits. Do not invent nodes to make a path."
        },
        {
          "id": "accessibility",
          "anchor": "accessibility",
          "heading": "Accessibility",
          "kind": "normative",
          "required": true,
          "purpose": "Keyboard, focus, reduced motion, captions/non-audio alternatives, contrast, and anything the feature itself publishes (e.g. semantic regions).",
          "text": "Transit and location need text labels (semantic layers or host UI). Do not rely on a minimap color."
        },
        {
          "id": "example",
          "anchor": "example",
          "heading": "Runnable example",
          "kind": "example",
          "required": true,
          "purpose": "Link canonical example IDs from the manifest. Embed and exact source come from the lock; this section must not paste a drifting snippet.",
          "text": "No canonical example yet. packages/engine/docs/world-graph.md and packages/engine/src/canvas/cyb-81-world-graph.repro.spec.ts are the runtime proof."
        },
        {
          "id": "expected-result",
          "anchor": "expected-result",
          "heading": "Expected result",
          "kind": "example",
          "required": true,
          "purpose": "What a reader should see, hear, or be able to do after the example runs.",
          "text": "A stable id round-trips; Canonical and known projections are distinct. Invalid transit is rejected."
        },
        {
          "id": "source",
          "anchor": "source",
          "heading": "Source, tests, and version",
          "kind": "provenance",
          "required": true,
          "purpose": "Links to implementationPath, tests, engine guide, and the pinned enginePackageVersion.",
          "text": "Implementation packages/engine/src/canvas/worldGraph.ts. Tests: packages/engine/src/canvas/worldGraph.spec.ts and packages/engine/src/canvas/cyb-81-world-graph.repro.spec.ts. Guide: packages/engine/docs/world-graph.md."
        },
        {
          "id": "related",
          "anchor": "related",
          "heading": "Related features",
          "kind": "tutorial",
          "required": true,
          "purpose": "Prerequisites first, then relatedFeatureIds. Use manifest ids, not free text names.",
          "text": "Related: world-patch, semantic-layers. Prerequisites: none."
        },
        {
          "id": "advanced",
          "anchor": "advanced",
          "heading": "Advanced composition",
          "kind": "tutorial",
          "required": false,
          "purpose": "Optional: combining this feature with runtime groups, compositor, bindings, or scenarios.",
          "text": ""
        },
        {
          "id": "version",
          "anchor": "version",
          "heading": "Version provenance",
          "kind": "provenance",
          "required": true,
          "purpose": "Engine package version this page describes, when the API first appeared, and a warning if the live package.json has moved on.",
          "text": "Ships in @cyberart-io/engine 0.0.9. First appeared in 0.0.9."
        }
      ]
    },
    {
      "id": "world-patch",
      "slug": "world-patch",
      "route": "/docs/features/world-patch",
      "title": "World patch",
      "purpose": "Transactional dry-run and atomic commit of a generated world patch as one revision.",
      "status": "substantial",
      "navGroup": "world-content",
      "packageName": "@cyberart-io/engine",
      "enginePackageVersion": "0.0.12",
      "compatibleEngineVersions": {
        "min": "0.0.9"
      },
      "documentationSource": {
        "kind": "site-page",
        "path": "src/docs/pages/world-patch.ts"
      },
      "exampleIds": [],
      "relatedFeatureIds": [
        "world-graph",
        "job-orchestration",
        "content-revision"
      ],
      "prerequisiteFeatureIds": [],
      "aliases": [],
      "keywords": [
        "world-patch",
        "createWorldPatchApplier",
        "createMemoryWorldPersistence"
      ],
      "surfaces": [
        "browser",
        "headless"
      ],
      "primaryExports": [
        "createWorldPatchApplier",
        "createMemoryWorldPersistence"
      ],
      "agentRoute": "/docs/agent/features/world-patch.json",
      "summary": "Transactional dry-run and atomic commit of a generated world patch as one revision.",
      "owningModule": "./canvas/worldPatch",
      "lifecycle": {
        "status": "substantial",
        "introducedIn": "0.0.9",
        "compatibleEngineVersions": {
          "min": "0.0.9"
        },
        "normative": true,
        "experimental": false,
        "deprecated": false,
        "successorFeatureIds": []
      },
      "evidence": {
        "implementationPath": "packages/engine/src/canvas/worldPatch.ts",
        "tests": [
          "packages/engine/src/canvas/worldPatch.spec.ts",
          "packages/engine/src/canvas/cyb-84-world-patch.repro.spec.ts"
        ],
        "engineGuide": "packages/engine/docs/world-patch.md"
      },
      "redirectsFrom": [],
      "examples": [],
      "pageSections": [
        {
          "id": "problem",
          "anchor": "problem",
          "heading": "What this solves",
          "kind": "tutorial",
          "required": true,
          "purpose": "Name the problem the feature exists to solve.",
          "text": "Generated world edits need a transactional dry-run and atomic commit as one revision, not piecemeal mutation."
        },
        {
          "id": "when-to-use",
          "anchor": "when-to-use",
          "heading": "When to use it",
          "kind": "tutorial",
          "required": true,
          "purpose": "Say when this is the right tool, and when a related feature is a better fit.",
          "text": "createWorldPatchApplier / createMemoryWorldPersistence when jobs produce a patch; world-graph is the target. content-revision is for content bundles, not graph patches."
        },
        {
          "id": "conceptual-model",
          "anchor": "conceptual-model",
          "heading": "Conceptual model",
          "kind": "tutorial",
          "required": true,
          "purpose": "Objects, states, and how they relate. Diagrams optional; names must match the API.",
          "text": "Dry-run then atomic commit; one revision. Host persistence adapter; fail closed on validation errors."
        },
        {
          "id": "lifecycle",
          "anchor": "lifecycle",
          "heading": "Lifecycle",
          "kind": "normative",
          "required": true,
          "purpose": "Create / attach / run / snapshot / restore / destroy, including which events fire.",
          "text": "createWorldPatchApplier → dry-run(patch) → commit → persist revision. Rollback is host-owned last-known-good, not a half-applied graph."
        },
        {
          "id": "cart-vs-host",
          "anchor": "cart-vs-host",
          "heading": "Cart versus host",
          "kind": "normative",
          "required": true,
          "purpose": "What the cart may declare or emit versus what the host/runtime must provide. Do not blur the boundary.",
          "text": "The host owns persistence and when to commit; workers produce patches as refs. Carts do not write nodes directly."
        },
        {
          "id": "contract",
          "anchor": "contract",
          "heading": "Minimal contract",
          "kind": "normative",
          "required": true,
          "purpose": "Smallest API/configuration surface that a correct integration uses, with types and defaults.",
          "text": "```ts\nimport { createWorldPatchApplier, createMemoryWorldPersistence } from '@cyberart-io/engine';\n\nconst applier = createWorldPatchApplier({ persistence: createMemoryWorldPersistence() });\n```\n\nPrimary exports: createWorldPatchApplier, createMemoryWorldPersistence."
        },
        {
          "id": "failure",
          "anchor": "failure",
          "heading": "Failure and fallback",
          "kind": "normative",
          "required": true,
          "purpose": "How the feature fails closed, what events/diagnostics look like, and what the host should do.",
          "text": "Dry-run errors mean no commit; partial apply is a bug. Missing persistence adapter fails closed."
        },
        {
          "id": "accessibility",
          "anchor": "accessibility",
          "heading": "Accessibility",
          "kind": "normative",
          "required": true,
          "purpose": "Keyboard, focus, reduced motion, captions/non-audio alternatives, contrast, and anything the feature itself publishes (e.g. semantic regions).",
          "text": "Revision status is host text. Preview vs committed should be labeled."
        },
        {
          "id": "example",
          "anchor": "example",
          "heading": "Runnable example",
          "kind": "example",
          "required": true,
          "purpose": "Link canonical example IDs from the manifest. Embed and exact source come from the lock; this section must not paste a drifting snippet.",
          "text": "No canonical example yet. packages/engine/docs/world-patch.md and packages/engine/src/canvas/cyb-84-world-patch.repro.spec.ts are the runtime proof."
        },
        {
          "id": "expected-result",
          "anchor": "expected-result",
          "heading": "Expected result",
          "kind": "example",
          "required": true,
          "purpose": "What a reader should see, hear, or be able to do after the example runs.",
          "text": "A valid patch dry-runs then commits as one revision. An invalid patch leaves the previous revision in place."
        },
        {
          "id": "source",
          "anchor": "source",
          "heading": "Source, tests, and version",
          "kind": "provenance",
          "required": true,
          "purpose": "Links to implementationPath, tests, engine guide, and the pinned enginePackageVersion.",
          "text": "Implementation packages/engine/src/canvas/worldPatch.ts. Tests: packages/engine/src/canvas/worldPatch.spec.ts and packages/engine/src/canvas/cyb-84-world-patch.repro.spec.ts. Guide: packages/engine/docs/world-patch.md."
        },
        {
          "id": "related",
          "anchor": "related",
          "heading": "Related features",
          "kind": "tutorial",
          "required": true,
          "purpose": "Prerequisites first, then relatedFeatureIds. Use manifest ids, not free text names.",
          "text": "Related: world-graph, job-orchestration, content-revision. Prerequisites: none."
        },
        {
          "id": "advanced",
          "anchor": "advanced",
          "heading": "Advanced composition",
          "kind": "tutorial",
          "required": false,
          "purpose": "Optional: combining this feature with runtime groups, compositor, bindings, or scenarios.",
          "text": ""
        },
        {
          "id": "version",
          "anchor": "version",
          "heading": "Version provenance",
          "kind": "provenance",
          "required": true,
          "purpose": "Engine package version this page describes, when the API first appeared, and a warning if the live package.json has moved on.",
          "text": "Ships in @cyberart-io/engine 0.0.9. First appeared in 0.0.9."
        }
      ]
    },
    {
      "id": "content-revision",
      "slug": "content-revision",
      "route": "/docs/features/content-revision",
      "title": "Content revision",
      "purpose": "Atomic staging and commit of an external content bundle with last-known-good rollback.",
      "status": "substantial",
      "navGroup": "world-content",
      "packageName": "@cyberart-io/engine",
      "enginePackageVersion": "0.0.12",
      "compatibleEngineVersions": {
        "min": "0.0.9"
      },
      "documentationSource": {
        "kind": "site-page",
        "path": "src/docs/pages/content-revision.ts"
      },
      "exampleIds": [],
      "relatedFeatureIds": [
        "remote-cart-manifest",
        "selection-trace",
        "asset-resolver"
      ],
      "prerequisiteFeatureIds": [],
      "aliases": [],
      "keywords": [
        "content-revision",
        "createContentRevisionActivator",
        "createStaticContentAdapter",
        "createRemoteContentAdapter"
      ],
      "surfaces": [
        "browser",
        "headless"
      ],
      "primaryExports": [
        "createContentRevisionActivator",
        "createStaticContentAdapter",
        "createRemoteContentAdapter"
      ],
      "agentRoute": "/docs/agent/features/content-revision.json",
      "summary": "Atomic staging and commit of an external content bundle with last-known-good rollback.",
      "owningModule": "./canvas/contentRevision",
      "lifecycle": {
        "status": "substantial",
        "introducedIn": "0.0.9",
        "compatibleEngineVersions": {
          "min": "0.0.9"
        },
        "normative": true,
        "experimental": false,
        "deprecated": false,
        "successorFeatureIds": []
      },
      "evidence": {
        "implementationPath": "packages/engine/src/canvas/contentRevision.ts",
        "tests": [
          "packages/engine/src/canvas/contentRevision.spec.ts",
          "packages/engine/src/canvas/cyb-79-content-revision.repro.spec.ts"
        ],
        "engineGuide": "packages/engine/docs/content-revision.md"
      },
      "redirectsFrom": [],
      "examples": [],
      "pageSections": [
        {
          "id": "problem",
          "anchor": "problem",
          "heading": "What this solves",
          "kind": "tutorial",
          "required": true,
          "purpose": "Name the problem the feature exists to solve.",
          "text": "External content bundles need atomic staging and commit with last-known-good rollback — not a live fetch mid-frame."
        },
        {
          "id": "when-to-use",
          "anchor": "when-to-use",
          "heading": "When to use it",
          "kind": "tutorial",
          "required": true,
          "purpose": "Say when this is the right tool, and when a related feature is a better fit.",
          "text": "createContentRevisionActivator / createStaticContentAdapter / createRemoteContentAdapter for bundle swap; remote-cart-manifest signs catalogs. asset-resolver loads declared files after activation."
        },
        {
          "id": "conceptual-model",
          "anchor": "conceptual-model",
          "heading": "Conceptual model",
          "kind": "tutorial",
          "required": true,
          "purpose": "Objects, states, and how they relate. Diagrams optional; names must match the API.",
          "text": "Stage → commit as one revision; last-known-good rollback. Static vs remote adapters; no ambient network from the cart."
        },
        {
          "id": "lifecycle",
          "anchor": "lifecycle",
          "heading": "Lifecycle",
          "kind": "normative",
          "required": true,
          "purpose": "Create / attach / run / snapshot / restore / destroy, including which events fire.",
          "text": "createContentRevisionActivator → stage bundle → commit → on failure rollback to last-known-good."
        },
        {
          "id": "cart-vs-host",
          "anchor": "cart-vs-host",
          "heading": "Cart versus host",
          "kind": "normative",
          "required": true,
          "purpose": "What the cart may declare or emit versus what the host/runtime must provide. Do not blur the boundary.",
          "text": "The host owns adapters, credentials policy, and rollback; carts consume activated content ids. Docs examples must remain credentials: none."
        },
        {
          "id": "contract",
          "anchor": "contract",
          "heading": "Minimal contract",
          "kind": "normative",
          "required": true,
          "purpose": "Smallest API/configuration surface that a correct integration uses, with types and defaults.",
          "text": "```ts\nimport { createContentRevisionActivator, createStaticContentAdapter } from '@cyberart-io/engine';\n\nconst activator = createContentRevisionActivator({ adapter: createStaticContentAdapter({ /* bundle */ }) });\n```\n\nPrimary exports: createContentRevisionActivator, createStaticContentAdapter, createRemoteContentAdapter."
        },
        {
          "id": "failure",
          "anchor": "failure",
          "heading": "Failure and fallback",
          "kind": "normative",
          "required": true,
          "purpose": "How the feature fails closed, what events/diagnostics look like, and what the host should do.",
          "text": "Failed commit rolls back; remote adapters still require host grants; deny-by-default. Do not leave a half-activated bundle."
        },
        {
          "id": "accessibility",
          "anchor": "accessibility",
          "heading": "Accessibility",
          "kind": "normative",
          "required": true,
          "purpose": "Keyboard, focus, reduced motion, captions/non-audio alternatives, contrast, and anything the feature itself publishes (e.g. semantic regions).",
          "text": "Announce content revision in host UI. Fallback to last-known-good should keep names/captions."
        },
        {
          "id": "example",
          "anchor": "example",
          "heading": "Runnable example",
          "kind": "example",
          "required": true,
          "purpose": "Link canonical example IDs from the manifest. Embed and exact source come from the lock; this section must not paste a drifting snippet.",
          "text": "No canonical example yet. packages/engine/docs/content-revision.md and packages/engine/src/canvas/cyb-79-content-revision.repro.spec.ts are the runtime proof."
        },
        {
          "id": "expected-result",
          "anchor": "expected-result",
          "heading": "Expected result",
          "kind": "example",
          "required": true,
          "purpose": "What a reader should see, hear, or be able to do after the example runs.",
          "text": "A staged bundle commits atomically. A failed commit restores last-known-good."
        },
        {
          "id": "source",
          "anchor": "source",
          "heading": "Source, tests, and version",
          "kind": "provenance",
          "required": true,
          "purpose": "Links to implementationPath, tests, engine guide, and the pinned enginePackageVersion.",
          "text": "Implementation packages/engine/src/canvas/contentRevision.ts. Tests: packages/engine/src/canvas/contentRevision.spec.ts and packages/engine/src/canvas/cyb-79-content-revision.repro.spec.ts. Guide: packages/engine/docs/content-revision.md."
        },
        {
          "id": "related",
          "anchor": "related",
          "heading": "Related features",
          "kind": "tutorial",
          "required": true,
          "purpose": "Prerequisites first, then relatedFeatureIds. Use manifest ids, not free text names.",
          "text": "Related: remote-cart-manifest, selection-trace, asset-resolver. Prerequisites: none."
        },
        {
          "id": "advanced",
          "anchor": "advanced",
          "heading": "Advanced composition",
          "kind": "tutorial",
          "required": false,
          "purpose": "Optional: combining this feature with runtime groups, compositor, bindings, or scenarios.",
          "text": ""
        },
        {
          "id": "version",
          "anchor": "version",
          "heading": "Version provenance",
          "kind": "provenance",
          "required": true,
          "purpose": "Engine package version this page describes, when the API first appeared, and a warning if the live package.json has moved on.",
          "text": "Ships in @cyberart-io/engine 0.0.9. First appeared in 0.0.9."
        }
      ]
    },
    {
      "id": "remote-cart-manifest",
      "slug": "remote-cart-manifest",
      "route": "/docs/features/remote-cart-manifest",
      "title": "Remote cart manifests",
      "purpose": "HMAC-signed versioned catalogs, declared asset hashes, deny-by-default host grants, no ambient network.",
      "status": "substantial",
      "navGroup": "world-content",
      "packageName": "@cyberart-io/engine",
      "enginePackageVersion": "0.0.12",
      "compatibleEngineVersions": {
        "min": "0.0.9"
      },
      "documentationSource": {
        "kind": "site-page",
        "path": "src/docs/pages/remote-cart-manifest.ts"
      },
      "exampleIds": [],
      "relatedFeatureIds": [
        "capability-manifest",
        "executable-modules",
        "content-revision"
      ],
      "prerequisiteFeatureIds": [],
      "aliases": [],
      "keywords": [
        "remote-cart-manifest",
        "defineRemoteCartManifest",
        "loadRemoteCart",
        "createRemoteCartSandbox",
        "createHostGrantSet"
      ],
      "surfaces": [
        "browser",
        "headless"
      ],
      "primaryExports": [
        "defineRemoteCartManifest",
        "loadRemoteCart",
        "createRemoteCartSandbox",
        "createHostGrantSet"
      ],
      "agentRoute": "/docs/agent/features/remote-cart-manifest.json",
      "summary": "HMAC-signed versioned catalogs, declared asset hashes, deny-by-default host grants, no ambient network.",
      "owningModule": "./canvas/remoteCartManifest",
      "lifecycle": {
        "status": "substantial",
        "introducedIn": "0.0.9",
        "compatibleEngineVersions": {
          "min": "0.0.9"
        },
        "normative": true,
        "experimental": false,
        "deprecated": false,
        "successorFeatureIds": []
      },
      "evidence": {
        "implementationPath": "packages/engine/src/canvas/remoteCartManifest.ts",
        "tests": [
          "packages/engine/src/canvas/remoteCartManifest.spec.ts",
          "packages/engine/src/canvas/cyb-74-remote-cart-manifest.repro.spec.ts"
        ],
        "engineGuide": "packages/engine/docs/remote-cart-manifest.md"
      },
      "redirectsFrom": [],
      "examples": [],
      "pageSections": [
        {
          "id": "problem",
          "anchor": "problem",
          "heading": "What this solves",
          "kind": "tutorial",
          "required": true,
          "purpose": "Name the problem the feature exists to solve.",
          "text": "Remote carts need HMAC-signed versioned catalogs, declared asset hashes, and deny-by-default grants — no ambient network."
        },
        {
          "id": "when-to-use",
          "anchor": "when-to-use",
          "heading": "When to use it",
          "kind": "tutorial",
          "required": true,
          "purpose": "Say when this is the right tool, and when a related feature is a better fit.",
          "text": "defineRemoteCartManifest / loadRemoteCart / createRemoteCartSandbox / createHostGrantSet when the host loads a catalog; executable-modules run allowlisted factories. capability-manifest is the per-cart document."
        },
        {
          "id": "conceptual-model",
          "anchor": "conceptual-model",
          "heading": "Conceptual model",
          "kind": "tutorial",
          "required": true,
          "purpose": "Objects, states, and how they relate. Diagrams optional; names must match the API.",
          "text": "HMAC-signed catalogs; declared asset hashes. Deny-by-default host grants; sandbox. No ambient network from the cart."
        },
        {
          "id": "lifecycle",
          "anchor": "lifecycle",
          "heading": "Lifecycle",
          "kind": "normative",
          "required": true,
          "purpose": "Create / attach / run / snapshot / restore / destroy, including which events fire.",
          "text": "defineRemoteCartManifest → verify signature and hashes → createHostGrantSet → loadRemoteCart into sandbox → revoke grants on destroy."
        },
        {
          "id": "cart-vs-host",
          "anchor": "cart-vs-host",
          "heading": "Cart versus host",
          "kind": "normative",
          "required": true,
          "purpose": "What the cart may declare or emit versus what the host/runtime must provide. Do not blur the boundary.",
          "text": "The host owns keys, grants, and fetch; carts never fetch. Docs examples stay network: none; this page does not ship a live remote catalog."
        },
        {
          "id": "contract",
          "anchor": "contract",
          "heading": "Minimal contract",
          "kind": "normative",
          "required": true,
          "purpose": "Smallest API/configuration surface that a correct integration uses, with types and defaults.",
          "text": "```ts\nimport { defineRemoteCartManifest, createHostGrantSet } from '@cyberart-io/engine';\n\nconst defined = defineRemoteCartManifest({ /* catalog */ });\nconst grants = createHostGrantSet({ /* deny by default */ });\n```\n\nPrimary exports: defineRemoteCartManifest, loadRemoteCart, createRemoteCartSandbox, createHostGrantSet."
        },
        {
          "id": "failure",
          "anchor": "failure",
          "heading": "Failure and fallback",
          "kind": "normative",
          "required": true,
          "purpose": "How the feature fails closed, what events/diagnostics look like, and what the host should do.",
          "text": "Bad HMAC, hash mismatch, or missing grants fail closed; Do not load on a warning. Unsigned catalogs are not a docs example."
        },
        {
          "id": "accessibility",
          "anchor": "accessibility",
          "heading": "Accessibility",
          "kind": "normative",
          "required": true,
          "purpose": "Keyboard, focus, reduced motion, captions/non-audio alternatives, contrast, and anything the feature itself publishes (e.g. semantic regions).",
          "text": "Load failures are host text. Sandbox restrictions are not a reason to omit captions on the cart that did load."
        },
        {
          "id": "example",
          "anchor": "example",
          "heading": "Runnable example",
          "kind": "example",
          "required": true,
          "purpose": "Link canonical example IDs from the manifest. Embed and exact source come from the lock; this section must not paste a drifting snippet.",
          "text": "No canonical example yet. packages/engine/docs/remote-cart-manifest.md and packages/engine/src/canvas/cyb-74-remote-cart-manifest.repro.spec.ts are the runtime proof."
        },
        {
          "id": "expected-result",
          "anchor": "expected-result",
          "heading": "Expected result",
          "kind": "example",
          "required": true,
          "purpose": "What a reader should see, hear, or be able to do after the example runs.",
          "text": "A valid signed catalog with grants loads; A hash mismatch does not. Empty grants deny the load."
        },
        {
          "id": "source",
          "anchor": "source",
          "heading": "Source, tests, and version",
          "kind": "provenance",
          "required": true,
          "purpose": "Links to implementationPath, tests, engine guide, and the pinned enginePackageVersion.",
          "text": "Implementation packages/engine/src/canvas/remoteCartManifest.ts. Tests: packages/engine/src/canvas/remoteCartManifest.spec.ts and packages/engine/src/canvas/cyb-74-remote-cart-manifest.repro.spec.ts. Guide: packages/engine/docs/remote-cart-manifest.md."
        },
        {
          "id": "related",
          "anchor": "related",
          "heading": "Related features",
          "kind": "tutorial",
          "required": true,
          "purpose": "Prerequisites first, then relatedFeatureIds. Use manifest ids, not free text names.",
          "text": "Related: capability-manifest, executable-modules, content-revision. Prerequisites: none."
        },
        {
          "id": "advanced",
          "anchor": "advanced",
          "heading": "Advanced composition",
          "kind": "tutorial",
          "required": false,
          "purpose": "Optional: combining this feature with runtime groups, compositor, bindings, or scenarios.",
          "text": ""
        },
        {
          "id": "version",
          "anchor": "version",
          "heading": "Version provenance",
          "kind": "provenance",
          "required": true,
          "purpose": "Engine package version this page describes, when the API first appeared, and a warning if the live package.json has moved on.",
          "text": "Ships in @cyberart-io/engine 0.0.9. First appeared in 0.0.9."
        }
      ]
    },
    {
      "id": "headless-harness",
      "slug": "headless-harness",
      "route": "/docs/features/headless-harness",
      "title": "Headless harness",
      "purpose": "Node/jsdom createHeadlessHarness around production createRuntime({ deterministic }), software Canvas2D, and pixel asserts.",
      "status": "substantial",
      "navGroup": "host-testing",
      "packageName": "@cyberart-io/engine",
      "enginePackageVersion": "0.0.12",
      "compatibleEngineVersions": {
        "min": "0.0.6"
      },
      "documentationSource": {
        "kind": "site-page",
        "path": "src/docs/pages/headless-harness.ts"
      },
      "exampleIds": [
        "calculation-capability"
      ],
      "relatedFeatureIds": [
        "deterministic-mode",
        "runtime-group",
        "frame-benchmark",
        "browser-harness",
        "production-scenario"
      ],
      "prerequisiteFeatureIds": [
        "runtime",
        "deterministic-mode"
      ],
      "aliases": [],
      "keywords": [
        "headless-harness",
        "createHeadlessHarness",
        "installHeadlessCanvas"
      ],
      "surfaces": [
        "headless"
      ],
      "primaryExports": [
        "createHeadlessHarness",
        "installHeadlessCanvas"
      ],
      "agentRoute": "/docs/agent/features/headless-harness.json",
      "summary": "Node/jsdom createHeadlessHarness around production createRuntime({ deterministic }), software Canvas2D, and pixel asserts.",
      "owningModule": "./canvas/headlessHarness",
      "lifecycle": {
        "status": "substantial",
        "introducedIn": "0.0.6",
        "compatibleEngineVersions": {
          "min": "0.0.6"
        },
        "normative": true,
        "experimental": false,
        "deprecated": false,
        "successorFeatureIds": []
      },
      "evidence": {
        "implementationPath": "packages/engine/src/canvas/headlessHarness.ts",
        "tests": [
          "packages/engine/src/canvas/headlessHarness.spec.ts",
          "packages/engine/src/canvas/cyb-57-exports.repro.spec.ts",
          "packages/engine/src/canvas/cyb-62-headless-canvas2d.repro.spec.ts"
        ],
        "engineGuide": "packages/engine/docs/headless-harness.md"
      },
      "redirectsFrom": [],
      "examples": [
        {
          "id": "calculation-capability",
          "route": "/docs/examples/calculation-capability",
          "title": "Calculation cart with capability manifest",
          "purpose": "Show a calculation cart that updates from host-channel events without a canvas, plus a versioned engine capability manifest the host can validate. Not a game.",
          "featureIds": [
            "calculation-carts",
            "capability-manifest",
            "headless-harness"
          ],
          "runtimeKind": "calculation",
          "artifact": {
            "state": "available",
            "reason": "Cache verified against the lock."
          },
          "source": {
            "state": "restricted",
            "reason": "Source not published; example-carts is private.",
            "files": [
              "cart.ts",
              "main.ts",
              "capabilities.json",
              "manifest.json"
            ]
          },
          "provenance": {
            "repository": "cyberart-io/example-carts",
            "commit": "2ed0eb599ded827cca46bcd2fc66dea78f387a0c",
            "engineVersion": "0.0.12",
            "sourceUrl": null
          },
          "capabilities": {
            "permissions": [
              "host.intent.*",
              "host.state.*"
            ],
            "assets": [],
            "network": "none",
            "credentials": "none",
            "engineCapabilityFile": "capabilities.json"
          },
          "interaction": {
            "mode": "scripted",
            "notes": "The host dispatches host.state.scene-changed. There is no pointer or keyboard surface."
          },
          "expected": {
            "visible": "No canvas. After a scene-changed event, cart state.sceneId matches the payload and the cart emits host.intent.overlay with overlayId banner.",
            "audible": "silent"
          },
          "accessibility": {
            "notes": "No visual surface. Docs should describe the event exchange in text.",
            "fallback": "State and emitted events are the accessible representation."
          },
          "viewport": {
            "width": 320,
            "height": 180
          }
        }
      ],
      "pageSections": [
        {
          "id": "problem",
          "anchor": "problem",
          "heading": "What this solves",
          "kind": "tutorial",
          "required": true,
          "purpose": "Name the problem the feature exists to solve.",
          "text": "CI and agents need production createRuntime({ deterministic }) with software Canvas2D and pixel asserts, without a browser window."
        },
        {
          "id": "when-to-use",
          "anchor": "when-to-use",
          "heading": "When to use it",
          "kind": "tutorial",
          "required": true,
          "purpose": "Say when this is the right tool, and when a related feature is a better fit.",
          "text": "Import createHeadlessHarness and installHeadlessCanvas from @cyberart-io/engine/headless; Use browser-harness for real DOM clicks and screenshots. Do not call installHeadlessCanvas from Player or kaleidoscope."
        },
        {
          "id": "conceptual-model",
          "anchor": "conceptual-model",
          "heading": "Conceptual model",
          "kind": "tutorial",
          "required": true,
          "purpose": "Objects, states, and how they relate. Diagrams optional; names must match the API.",
          "text": "The harness wraps production createRuntime({ deterministic }) plus software Canvas2D; step/schedule/key/click/inspect/captureFrame are the CI surface. Headless Canvas2D and visual-assert are supporting modules on this page’s parent cluster, not separate docs pages."
        },
        {
          "id": "lifecycle",
          "anchor": "lifecycle",
          "heading": "Lifecycle",
          "kind": "normative",
          "required": true,
          "purpose": "Create / attach / run / snapshot / restore / destroy, including which events fire.",
          "text": "createHeadlessHarness({ cart, seed, width, height, origin }) → step/key/click → inspect / captureFrame → destroy. installHeadlessCanvas is idempotent and mutates HTMLCanvasElement.prototype."
        },
        {
          "id": "cart-vs-host",
          "anchor": "cart-vs-host",
          "heading": "Cart versus host",
          "kind": "normative",
          "required": true,
          "purpose": "What the cart may declare or emit versus what the host/runtime must provide. Do not blur the boundary.",
          "text": "Tests own the harness; the cart is unchanged production code. Pixel identity uses compareImageData / assertPixelsEqual — supporting visual-assert, documented here."
        },
        {
          "id": "contract",
          "anchor": "contract",
          "heading": "Minimal contract",
          "kind": "normative",
          "required": true,
          "purpose": "Smallest API/configuration surface that a correct integration uses, with types and defaults.",
          "text": "```ts\nimport { createHeadlessHarness } from '@cyberart-io/engine/headless';\n\nconst harness = createHeadlessHarness({ cart, seed, width: 320, height: 180, origin: 0 });\nawait harness.step(3);\nconst inspect = await harness.inspect();\nharness.destroy();\n```\n\nkind: \"calculation\" uses createRuntime directly with installHeadlessCanvas when there is no paint."
        },
        {
          "id": "failure",
          "anchor": "failure",
          "heading": "Failure and fallback",
          "kind": "normative",
          "required": true,
          "purpose": "How the feature fails closed, what events/diagnostics look like, and what the host should do.",
          "text": "Using the harness after destroy throws; Headless Canvas2D in 0.0.9 parses hex/rgb fills, not hsl(). Capture before a 2D context exists returns a 1×1 PNG fallback; Do not treat wall-clock Date.now() as deterministic."
        },
        {
          "id": "accessibility",
          "anchor": "accessibility",
          "heading": "Accessibility",
          "kind": "normative",
          "required": true,
          "purpose": "Keyboard, focus, reduced motion, captions/non-audio alternatives, contrast, and anything the feature itself publishes (e.g. semantic regions).",
          "text": "Headless output is inspect state, events, and optional PNG; describe expected pixels in text. This is not a user-facing surface."
        },
        {
          "id": "example",
          "anchor": "example",
          "heading": "Runnable example",
          "kind": "example",
          "required": true,
          "purpose": "Link canonical example IDs from the manifest. Embed and exact source come from the lock; this section must not paste a drifting snippet.",
          "text": "Canonical example calculation-capability proves the calculation + deterministic path used by headless CI. This page does not copy cart.ts."
        },
        {
          "id": "expected-result",
          "anchor": "expected-result",
          "heading": "Expected result",
          "kind": "example",
          "required": true,
          "purpose": "What a reader should see, hear, or be able to do after the example runs.",
          "text": "step(1) after scene-changed yields state.sceneId alpha and an overlay intent, with no canvas element."
        },
        {
          "id": "source",
          "anchor": "source",
          "heading": "Source, tests, and version",
          "kind": "provenance",
          "required": true,
          "purpose": "Links to implementationPath, tests, engine guide, and the pinned enginePackageVersion.",
          "text": "Implementation packages/engine/src/canvas/headlessHarness.ts. Tests: packages/engine/src/canvas/headlessHarness.spec.ts, packages/engine/src/canvas/cyb-57-exports.repro.spec.ts, packages/engine/src/canvas/cyb-62-headless-canvas2d.repro.spec.ts. Guide: packages/engine/docs/headless-harness.md."
        },
        {
          "id": "related",
          "anchor": "related",
          "heading": "Related features",
          "kind": "tutorial",
          "required": true,
          "purpose": "Prerequisites first, then relatedFeatureIds. Use manifest ids, not free text names.",
          "text": "Prerequisites: runtime, deterministic-mode. Related: runtime-group, frame-benchmark, browser-harness, production-scenario."
        },
        {
          "id": "advanced",
          "anchor": "advanced",
          "heading": "Advanced composition",
          "kind": "tutorial",
          "required": false,
          "purpose": "Optional: combining this feature with runtime groups, compositor, bindings, or scenarios.",
          "text": "createHeadlessMultiCartHarness is the group API for tests (supporting headless-multi-cart, parent runtime-group)."
        },
        {
          "id": "version",
          "anchor": "version",
          "heading": "Version provenance",
          "kind": "provenance",
          "required": true,
          "purpose": "Engine package version this page describes, when the API first appeared, and a warning if the live package.json has moved on.",
          "text": "Ships in @cyberart-io/engine 0.0.9. First appeared in 0.0.6."
        }
      ]
    },
    {
      "id": "frame-benchmark",
      "slug": "frame-benchmark",
      "route": "/docs/features/frame-benchmark",
      "title": "Frame benchmarking",
      "purpose": "benchmarkCartFrames (headless) and optional onFrameTiming (live); zero cost when unset.",
      "status": "substantial",
      "navGroup": "host-testing",
      "packageName": "@cyberart-io/engine",
      "enginePackageVersion": "0.0.12",
      "compatibleEngineVersions": {
        "min": "0.0.8"
      },
      "documentationSource": {
        "kind": "site-page",
        "path": "src/docs/pages/frame-benchmark.ts"
      },
      "exampleIds": [],
      "relatedFeatureIds": [
        "headless-harness",
        "runtime"
      ],
      "prerequisiteFeatureIds": [],
      "aliases": [],
      "keywords": [
        "frame-benchmark",
        "benchmarkCartFrames",
        "formatFrameBenchmarkResult"
      ],
      "surfaces": [
        "headless"
      ],
      "primaryExports": [
        "benchmarkCartFrames",
        "formatFrameBenchmarkResult"
      ],
      "agentRoute": "/docs/agent/features/frame-benchmark.json",
      "summary": "benchmarkCartFrames (headless) and optional onFrameTiming (live); zero cost when unset.",
      "owningModule": "./canvas/frameBenchmark",
      "lifecycle": {
        "status": "substantial",
        "introducedIn": "0.0.8",
        "compatibleEngineVersions": {
          "min": "0.0.8"
        },
        "normative": true,
        "experimental": false,
        "deprecated": false,
        "successorFeatureIds": []
      },
      "evidence": {
        "implementationPath": "packages/engine/src/canvas/frameBenchmark.ts",
        "tests": [
          "packages/engine/src/canvas/frameBenchmark.spec.ts"
        ],
        "engineGuide": "packages/engine/docs/frame-benchmark.md"
      },
      "redirectsFrom": [],
      "examples": [],
      "pageSections": [
        {
          "id": "problem",
          "anchor": "problem",
          "heading": "What this solves",
          "kind": "tutorial",
          "required": true,
          "purpose": "Name the problem the feature exists to solve.",
          "text": "Hosts need frame timings in CI and an optional live callback that costs nothing when unset."
        },
        {
          "id": "when-to-use",
          "anchor": "when-to-use",
          "heading": "When to use it",
          "kind": "tutorial",
          "required": true,
          "purpose": "Say when this is the right tool, and when a related feature is a better fit.",
          "text": "benchmarkCartFrames from headless; onFrameTiming on a live runtime only when you opted in. This is not a substitute for production-scenario evidence."
        },
        {
          "id": "conceptual-model",
          "anchor": "conceptual-model",
          "heading": "Conceptual model",
          "kind": "tutorial",
          "required": true,
          "purpose": "Objects, states, and how they relate. Diagrams optional; names must match the API.",
          "text": "benchmarkCartFrames (headless) and optional onFrameTiming (live); zero cost when unset. formatFrameBenchmarkResult for text."
        },
        {
          "id": "lifecycle",
          "anchor": "lifecycle",
          "heading": "Lifecycle",
          "kind": "normative",
          "required": true,
          "purpose": "Create / attach / run / snapshot / restore / destroy, including which events fire.",
          "text": "create harness → benchmarkCartFrames({ cart, frames }) → read result → destroy. Live: pass onFrameTiming or omit it."
        },
        {
          "id": "cart-vs-host",
          "anchor": "cart-vs-host",
          "heading": "Cart versus host",
          "kind": "normative",
          "required": true,
          "purpose": "What the cart may declare or emit versus what the host/runtime must provide. Do not blur the boundary.",
          "text": "The host opts into timing; the cart should not log performance. Unset callback means no work on the hot path."
        },
        {
          "id": "contract",
          "anchor": "contract",
          "heading": "Minimal contract",
          "kind": "normative",
          "required": true,
          "purpose": "Smallest API/configuration surface that a correct integration uses, with types and defaults.",
          "text": "```ts\nimport { benchmarkCartFrames, formatFrameBenchmarkResult } from '@cyberart-io/engine';\n\nconst result = await benchmarkCartFrames({ cart, frames: 30 });\n```\n\nPrimary exports: benchmarkCartFrames, formatFrameBenchmarkResult."
        },
        {
          "id": "failure",
          "anchor": "failure",
          "heading": "Failure and fallback",
          "kind": "normative",
          "required": true,
          "purpose": "How the feature fails closed, what events/diagnostics look like, and what the host should do.",
          "text": "A throwing cart fails the benchmark like any other harness error; Do not treat NaN timings as zero. Live callback errors must not take down the loop unless the host so chooses."
        },
        {
          "id": "accessibility",
          "anchor": "accessibility",
          "heading": "Accessibility",
          "kind": "normative",
          "required": true,
          "purpose": "Keyboard, focus, reduced motion, captions/non-audio alternatives, contrast, and anything the feature itself publishes (e.g. semantic regions).",
          "text": "Benchmark output is developer-facing text. Not a user surface."
        },
        {
          "id": "example",
          "anchor": "example",
          "heading": "Runnable example",
          "kind": "example",
          "required": true,
          "purpose": "Link canonical example IDs from the manifest. Embed and exact source come from the lock; this section must not paste a drifting snippet.",
          "text": "No canonical example yet. packages/engine/docs/frame-benchmark.md and packages/engine/src/canvas/frameBenchmark.spec.ts are the runtime proof."
        },
        {
          "id": "expected-result",
          "anchor": "expected-result",
          "heading": "Expected result",
          "kind": "example",
          "required": true,
          "purpose": "What a reader should see, hear, or be able to do after the example runs.",
          "text": "benchmarkCartFrames returns a result that formatFrameBenchmarkResult can print. Omitting onFrameTiming adds no per-frame work."
        },
        {
          "id": "source",
          "anchor": "source",
          "heading": "Source, tests, and version",
          "kind": "provenance",
          "required": true,
          "purpose": "Links to implementationPath, tests, engine guide, and the pinned enginePackageVersion.",
          "text": "Implementation packages/engine/src/canvas/frameBenchmark.ts. Tests: packages/engine/src/canvas/frameBenchmark.spec.ts. Guide: packages/engine/docs/frame-benchmark.md."
        },
        {
          "id": "related",
          "anchor": "related",
          "heading": "Related features",
          "kind": "tutorial",
          "required": true,
          "purpose": "Prerequisites first, then relatedFeatureIds. Use manifest ids, not free text names.",
          "text": "Related: headless-harness, runtime. Prerequisites: none."
        },
        {
          "id": "advanced",
          "anchor": "advanced",
          "heading": "Advanced composition",
          "kind": "tutorial",
          "required": false,
          "purpose": "Optional: combining this feature with runtime groups, compositor, bindings, or scenarios.",
          "text": ""
        },
        {
          "id": "version",
          "anchor": "version",
          "heading": "Version provenance",
          "kind": "provenance",
          "required": true,
          "purpose": "Engine package version this page describes, when the API first appeared, and a warning if the live package.json has moved on.",
          "text": "Ships in @cyberart-io/engine 0.0.9. First appeared in 0.0.8."
        }
      ]
    },
    {
      "id": "browser-harness",
      "slug": "browser-harness",
      "route": "/docs/features/browser-harness",
      "title": "Browser harness",
      "purpose": "createBrowserHarness: DOM clicks, viewport/DPR, composed screenshots, Playwright-compatible adapter.",
      "status": "substantial",
      "navGroup": "host-testing",
      "packageName": "@cyberart-io/engine",
      "enginePackageVersion": "0.0.12",
      "compatibleEngineVersions": {
        "min": "0.0.6"
      },
      "documentationSource": {
        "kind": "site-page",
        "path": "src/docs/pages/browser-harness.ts"
      },
      "exampleIds": [],
      "relatedFeatureIds": [
        "headless-harness",
        "production-scenario",
        "compositor"
      ],
      "prerequisiteFeatureIds": [],
      "aliases": [],
      "keywords": [
        "browser-harness",
        "createBrowserHarness",
        "createPlaywrightCompatibleAdapter"
      ],
      "surfaces": [
        "browser"
      ],
      "primaryExports": [
        "createBrowserHarness",
        "createPlaywrightCompatibleAdapter"
      ],
      "agentRoute": "/docs/agent/features/browser-harness.json",
      "summary": "createBrowserHarness: DOM clicks, viewport/DPR, composed screenshots, Playwright-compatible adapter.",
      "owningModule": "./canvas/browserHarness",
      "lifecycle": {
        "status": "substantial",
        "introducedIn": "0.0.6",
        "compatibleEngineVersions": {
          "min": "0.0.6"
        },
        "normative": true,
        "experimental": false,
        "deprecated": false,
        "successorFeatureIds": []
      },
      "evidence": {
        "implementationPath": "packages/engine/src/canvas/browserHarness.ts",
        "tests": [
          "packages/engine/src/canvas/browserHarness.spec.ts",
          "packages/engine/src/canvas/cyb-63-browser-harness.repro.spec.ts"
        ],
        "engineGuide": "packages/engine/docs/browser-harness.md"
      },
      "redirectsFrom": [],
      "examples": [],
      "pageSections": [
        {
          "id": "problem",
          "anchor": "problem",
          "heading": "What this solves",
          "kind": "tutorial",
          "required": true,
          "purpose": "Name the problem the feature exists to solve.",
          "text": "Some proofs need a real DOM, clicks, viewport/DPR, and composed screenshots — more than software Canvas2D."
        },
        {
          "id": "when-to-use",
          "anchor": "when-to-use",
          "heading": "When to use it",
          "kind": "tutorial",
          "required": true,
          "purpose": "Say when this is the right tool, and when a related feature is a better fit.",
          "text": "createBrowserHarness / createPlaywrightCompatibleAdapter in a browser; headless-harness is the Node/jsdom default. Playwright adapter is supporting, documented here."
        },
        {
          "id": "conceptual-model",
          "anchor": "conceptual-model",
          "heading": "Conceptual model",
          "kind": "tutorial",
          "required": true,
          "purpose": "Objects, states, and how they relate. Diagrams optional; names must match the API.",
          "text": "DOM clicks, viewport/DPR, composed screenshots; playwright-compatible adapter types. Same production runtime underneath."
        },
        {
          "id": "lifecycle",
          "anchor": "lifecycle",
          "heading": "Lifecycle",
          "kind": "normative",
          "required": true,
          "purpose": "Create / attach / run / snapshot / restore / destroy, including which events fire.",
          "text": "createBrowserHarness → set viewport → click → screenshot → destroy. Adapter maps to Playwright fixtures when the host chooses."
        },
        {
          "id": "cart-vs-host",
          "anchor": "cart-vs-host",
          "heading": "Cart versus host",
          "kind": "normative",
          "required": true,
          "purpose": "What the cart may declare or emit versus what the host/runtime must provide. Do not blur the boundary.",
          "text": "The test host owns the page; the cart is production code. Do not add always-on shims to make the harness green."
        },
        {
          "id": "contract",
          "anchor": "contract",
          "heading": "Minimal contract",
          "kind": "normative",
          "required": true,
          "purpose": "Smallest API/configuration surface that a correct integration uses, with types and defaults.",
          "text": "```ts\nimport { createBrowserHarness, createPlaywrightCompatibleAdapter } from '@cyberart-io/engine';\n\nconst harness = createBrowserHarness({ /* page / container */ });\n```\n\nPrimary exports: createBrowserHarness, createPlaywrightCompatibleAdapter."
        },
        {
          "id": "failure",
          "anchor": "failure",
          "heading": "Failure and fallback",
          "kind": "normative",
          "required": true,
          "purpose": "How the feature fails closed, what events/diagnostics look like, and what the host should do.",
          "text": "Missing canvas or wrong DPR fails the screenshot assertion; clicks are CSS vs canvas as documented — do not mix them. Playwright adapter is types/thin; it is not a second runtime."
        },
        {
          "id": "accessibility",
          "anchor": "accessibility",
          "heading": "Accessibility",
          "kind": "normative",
          "required": true,
          "purpose": "Keyboard, focus, reduced motion, captions/non-audio alternatives, contrast, and anything the feature itself publishes (e.g. semantic regions).",
          "text": "Browser tests should still keyboard-operate host controls. Screenshots are not an a11y oracle."
        },
        {
          "id": "example",
          "anchor": "example",
          "heading": "Runnable example",
          "kind": "example",
          "required": true,
          "purpose": "Link canonical example IDs from the manifest. Embed and exact source come from the lock; this section must not paste a drifting snippet.",
          "text": "No canonical example yet. packages/engine/docs/browser-harness.md and packages/engine/src/canvas/cyb-63-browser-harness.repro.spec.ts are the runtime proof."
        },
        {
          "id": "expected-result",
          "anchor": "expected-result",
          "heading": "Expected result",
          "kind": "example",
          "required": true,
          "purpose": "What a reader should see, hear, or be able to do after the example runs.",
          "text": "A click at documented coordinates reaches the cart; A composed screenshot is a PNG. Viewport/DPR match the harness options."
        },
        {
          "id": "source",
          "anchor": "source",
          "heading": "Source, tests, and version",
          "kind": "provenance",
          "required": true,
          "purpose": "Links to implementationPath, tests, engine guide, and the pinned enginePackageVersion.",
          "text": "Implementation packages/engine/src/canvas/browserHarness.ts. Tests: packages/engine/src/canvas/browserHarness.spec.ts and packages/engine/src/canvas/cyb-63-browser-harness.repro.spec.ts. Guide: packages/engine/docs/browser-harness.md."
        },
        {
          "id": "related",
          "anchor": "related",
          "heading": "Related features",
          "kind": "tutorial",
          "required": true,
          "purpose": "Prerequisites first, then relatedFeatureIds. Use manifest ids, not free text names.",
          "text": "Related: headless-harness, production-scenario, compositor. Prerequisites: none."
        },
        {
          "id": "advanced",
          "anchor": "advanced",
          "heading": "Advanced composition",
          "kind": "tutorial",
          "required": false,
          "purpose": "Optional: combining this feature with runtime groups, compositor, bindings, or scenarios.",
          "text": ""
        },
        {
          "id": "version",
          "anchor": "version",
          "heading": "Version provenance",
          "kind": "provenance",
          "required": true,
          "purpose": "Engine package version this page describes, when the API first appeared, and a warning if the live package.json has moved on.",
          "text": "Ships in @cyberart-io/engine 0.0.9. First appeared in 0.0.6."
        }
      ]
    },
    {
      "id": "production-scenario",
      "slug": "production-scenario",
      "route": "/docs/features/production-scenario",
      "title": "Production scenario",
      "purpose": "One JSON scenario drives the real host/runtime/cart composition; evidence bundles record firstBrokenBoundary.",
      "status": "substantial",
      "navGroup": "host-testing",
      "packageName": "@cyberart-io/engine",
      "enginePackageVersion": "0.0.12",
      "compatibleEngineVersions": {
        "min": "0.0.9"
      },
      "documentationSource": {
        "kind": "site-page",
        "path": "src/docs/pages/production-scenario.ts"
      },
      "exampleIds": [],
      "relatedFeatureIds": [
        "browser-harness",
        "runtime-group",
        "compositor",
        "presentation-bindings",
        "selection-trace"
      ],
      "prerequisiteFeatureIds": [],
      "aliases": [],
      "keywords": [
        "production-scenario",
        "defineProductionScenario",
        "createProductionScenarioRunner"
      ],
      "surfaces": [
        "browser",
        "headless"
      ],
      "primaryExports": [
        "defineProductionScenario",
        "createProductionScenarioRunner"
      ],
      "agentRoute": "/docs/agent/features/production-scenario.json",
      "summary": "One JSON scenario drives the real host/runtime/cart composition; evidence bundles record firstBrokenBoundary.",
      "owningModule": "./canvas/productionScenario",
      "lifecycle": {
        "status": "substantial",
        "introducedIn": "0.0.9",
        "compatibleEngineVersions": {
          "min": "0.0.9"
        },
        "normative": true,
        "experimental": false,
        "deprecated": false,
        "successorFeatureIds": []
      },
      "evidence": {
        "implementationPath": "packages/engine/src/canvas/productionScenario.ts",
        "tests": [
          "packages/engine/src/canvas/productionScenario.spec.ts",
          "packages/engine/src/canvas/cyb-82-production-scenario.repro.spec.ts"
        ],
        "engineGuide": "packages/engine/docs/production-scenario.md"
      },
      "redirectsFrom": [],
      "examples": [],
      "pageSections": [
        {
          "id": "problem",
          "anchor": "problem",
          "heading": "What this solves",
          "kind": "tutorial",
          "required": true,
          "purpose": "Name the problem the feature exists to solve.",
          "text": "CI needs one JSON scenario that drives the real host/runtime/cart composition, with evidence bundles that record firstBrokenBoundary — not a toy stub of production."
        },
        {
          "id": "when-to-use",
          "anchor": "when-to-use",
          "heading": "When to use it",
          "kind": "tutorial",
          "required": true,
          "purpose": "Say when this is the right tool, and when a related feature is a better fit.",
          "text": "defineProductionScenario / createProductionScenarioRunner for full-composition proofs; headless-harness for a single cart. selection-trace attaches why a choice won."
        },
        {
          "id": "conceptual-model",
          "anchor": "conceptual-model",
          "heading": "Conceptual model",
          "kind": "tutorial",
          "required": true,
          "purpose": "Objects, states, and how they relate. Diagrams optional; names must match the API.",
          "text": "One JSON scenario; real host/runtime/cart composition. Evidence bundles; firstBrokenBoundary."
        },
        {
          "id": "lifecycle",
          "anchor": "lifecycle",
          "heading": "Lifecycle",
          "kind": "normative",
          "required": true,
          "purpose": "Create / attach / run / snapshot / restore / destroy, including which events fire.",
          "text": "defineProductionScenario → createProductionScenarioRunner → run → read evidence → fail on firstBrokenBoundary as the host asserts."
        },
        {
          "id": "cart-vs-host",
          "anchor": "cart-vs-host",
          "heading": "Cart versus host",
          "kind": "normative",
          "required": true,
          "purpose": "What the cart may declare or emit versus what the host/runtime must provide. Do not blur the boundary.",
          "text": "The scenario is host data; the runner uses production constructors. Carts do not know they are in a scenario."
        },
        {
          "id": "contract",
          "anchor": "contract",
          "heading": "Minimal contract",
          "kind": "normative",
          "required": true,
          "purpose": "Smallest API/configuration surface that a correct integration uses, with types and defaults.",
          "text": "```ts\nimport { defineProductionScenario, createProductionScenarioRunner } from '@cyberart-io/engine';\n\nconst defined = defineProductionScenario({ /* json */ });\nconst runner = createProductionScenarioRunner(defined.scenario);\n```\n\nPrimary exports: defineProductionScenario, createProductionScenarioRunner."
        },
        {
          "id": "failure",
          "anchor": "failure",
          "heading": "Failure and fallback",
          "kind": "normative",
          "required": true,
          "purpose": "How the feature fails closed, what events/diagnostics look like, and what the host should do.",
          "text": "firstBrokenBoundary is the evidence field — do not hide it; Invalid scenario JSON fails closed before run. A green run without evidence is not this feature."
        },
        {
          "id": "accessibility",
          "anchor": "accessibility",
          "heading": "Accessibility",
          "kind": "normative",
          "required": true,
          "purpose": "Keyboard, focus, reduced motion, captions/non-audio alternatives, contrast, and anything the feature itself publishes (e.g. semantic regions).",
          "text": "Scenario evidence is developer-facing. User a11y still comes from semantic-layers and captions inside the composition."
        },
        {
          "id": "example",
          "anchor": "example",
          "heading": "Runnable example",
          "kind": "example",
          "required": true,
          "purpose": "Link canonical example IDs from the manifest. Embed and exact source come from the lock; this section must not paste a drifting snippet.",
          "text": "No canonical example yet. packages/engine/docs/production-scenario.md and packages/engine/src/canvas/cyb-82-production-scenario.repro.spec.ts are the runtime proof."
        },
        {
          "id": "expected-result",
          "anchor": "expected-result",
          "heading": "Expected result",
          "kind": "example",
          "required": true,
          "purpose": "What a reader should see, hear, or be able to do after the example runs.",
          "text": "A valid scenario runs the real composition. A broken boundary is named in evidence, not swallowed."
        },
        {
          "id": "source",
          "anchor": "source",
          "heading": "Source, tests, and version",
          "kind": "provenance",
          "required": true,
          "purpose": "Links to implementationPath, tests, engine guide, and the pinned enginePackageVersion.",
          "text": "Implementation packages/engine/src/canvas/productionScenario.ts. Tests: packages/engine/src/canvas/productionScenario.spec.ts and packages/engine/src/canvas/cyb-82-production-scenario.repro.spec.ts. Guide: packages/engine/docs/production-scenario.md."
        },
        {
          "id": "related",
          "anchor": "related",
          "heading": "Related features",
          "kind": "tutorial",
          "required": true,
          "purpose": "Prerequisites first, then relatedFeatureIds. Use manifest ids, not free text names.",
          "text": "Related: browser-harness, runtime-group, compositor, presentation-bindings, selection-trace. Prerequisites: none."
        },
        {
          "id": "advanced",
          "anchor": "advanced",
          "heading": "Advanced composition",
          "kind": "tutorial",
          "required": false,
          "purpose": "Optional: combining this feature with runtime groups, compositor, bindings, or scenarios.",
          "text": ""
        },
        {
          "id": "version",
          "anchor": "version",
          "heading": "Version provenance",
          "kind": "provenance",
          "required": true,
          "purpose": "Engine package version this page describes, when the API first appeared, and a warning if the live package.json has moved on.",
          "text": "Ships in @cyberart-io/engine 0.0.9. First appeared in 0.0.9."
        }
      ]
    },
    {
      "id": "replay-inspector",
      "slug": "replay-inspector",
      "route": "/docs/features/replay-inspector",
      "title": "Replay inspector",
      "purpose": "Causation trees, redacted export/import, and headless replay of recorded traces.",
      "status": "substantial",
      "navGroup": "host-testing",
      "packageName": "@cyberart-io/engine",
      "enginePackageVersion": "0.0.12",
      "compatibleEngineVersions": {
        "min": "0.0.6"
      },
      "documentationSource": {
        "kind": "site-page",
        "path": "src/docs/pages/replay-inspector.ts"
      },
      "exampleIds": [],
      "relatedFeatureIds": [
        "events",
        "deterministic-mode",
        "selection-trace"
      ],
      "prerequisiteFeatureIds": [],
      "aliases": [],
      "keywords": [
        "replay-inspector",
        "createReplayInspector",
        "compareReplayTraces",
        "replayExportedTrace"
      ],
      "surfaces": [
        "browser",
        "headless"
      ],
      "primaryExports": [
        "createReplayInspector",
        "compareReplayTraces",
        "replayExportedTrace"
      ],
      "agentRoute": "/docs/agent/features/replay-inspector.json",
      "summary": "Causation trees, redacted export/import, and headless replay of recorded traces.",
      "owningModule": "./canvas/replayInspector",
      "lifecycle": {
        "status": "substantial",
        "introducedIn": "0.0.6",
        "compatibleEngineVersions": {
          "min": "0.0.6"
        },
        "normative": true,
        "experimental": false,
        "deprecated": false,
        "successorFeatureIds": []
      },
      "evidence": {
        "implementationPath": "packages/engine/src/canvas/replayInspector.ts",
        "tests": [
          "packages/engine/src/canvas/replayInspector.spec.ts",
          "packages/engine/src/canvas/cyb-65-replay-inspector.repro.spec.ts"
        ],
        "engineGuide": "packages/engine/docs/replay-inspector.md"
      },
      "redirectsFrom": [],
      "examples": [],
      "pageSections": [
        {
          "id": "problem",
          "anchor": "problem",
          "heading": "What this solves",
          "kind": "tutorial",
          "required": true,
          "purpose": "Name the problem the feature exists to solve.",
          "text": "Recorded traces need causation trees, redacted export/import, and headless replay — including secret-safe output."
        },
        {
          "id": "when-to-use",
          "anchor": "when-to-use",
          "heading": "When to use it",
          "kind": "tutorial",
          "required": true,
          "purpose": "Say when this is the right tool, and when a related feature is a better fit.",
          "text": "createReplayInspector / compareReplayTraces / replayExportedTrace for debugging replay; deterministic-mode is the clock. selection-trace is choice provenance, not the full causation tree."
        },
        {
          "id": "conceptual-model",
          "anchor": "conceptual-model",
          "heading": "Conceptual model",
          "kind": "tutorial",
          "required": true,
          "purpose": "Objects, states, and how they relate. Diagrams optional; names must match the API.",
          "text": "Causation trees; redacted export/import. Headless replay of recorded traces; secrets do not belong in the export."
        },
        {
          "id": "lifecycle",
          "anchor": "lifecycle",
          "heading": "Lifecycle",
          "kind": "normative",
          "required": true,
          "purpose": "Create / attach / run / snapshot / restore / destroy, including which events fire.",
          "text": "createReplayInspector → record → export (redacted) → import → replayExportedTrace / compareReplayTraces."
        },
        {
          "id": "cart-vs-host",
          "anchor": "cart-vs-host",
          "heading": "Cart versus host",
          "kind": "normative",
          "required": true,
          "purpose": "What the cart may declare or emit versus what the host/runtime must provide. Do not blur the boundary.",
          "text": "The host owns what is redacted; carts emit events. Do not log tokens into traces; compareReplayTraces is data, not a throw into rAF."
        },
        {
          "id": "contract",
          "anchor": "contract",
          "heading": "Minimal contract",
          "kind": "normative",
          "required": true,
          "purpose": "Smallest API/configuration surface that a correct integration uses, with types and defaults.",
          "text": "```ts\nimport { createReplayInspector, compareReplayTraces, replayExportedTrace } from '@cyberart-io/engine';\n\nconst inspector = createReplayInspector();\n```\n\nPrimary exports: createReplayInspector, compareReplayTraces, replayExportedTrace."
        },
        {
          "id": "failure",
          "anchor": "failure",
          "heading": "Failure and fallback",
          "kind": "normative",
          "required": true,
          "purpose": "How the feature fails closed, what events/diagnostics look like, and what the host should do.",
          "text": "Mismatches are compareReplayTraces results; unredacted secrets in an export are a host bug. Missing frames fail replay closed."
        },
        {
          "id": "accessibility",
          "anchor": "accessibility",
          "heading": "Accessibility",
          "kind": "normative",
          "required": true,
          "purpose": "Keyboard, focus, reduced motion, captions/non-audio alternatives, contrast, and anything the feature itself publishes (e.g. semantic regions).",
          "text": "Inspector output is developer-facing. Redaction is a privacy requirement, not optional polish."
        },
        {
          "id": "example",
          "anchor": "example",
          "heading": "Runnable example",
          "kind": "example",
          "required": true,
          "purpose": "Link canonical example IDs from the manifest. Embed and exact source come from the lock; this section must not paste a drifting snippet.",
          "text": "No canonical example yet. packages/engine/docs/replay-inspector.md and packages/engine/src/canvas/cyb-65-replay-inspector.repro.spec.ts are the runtime proof."
        },
        {
          "id": "expected-result",
          "anchor": "expected-result",
          "heading": "Expected result",
          "kind": "example",
          "required": true,
          "purpose": "What a reader should see, hear, or be able to do after the example runs.",
          "text": "An exported trace replays headlessly; compareReplayTraces reports diffs. Redacted fields are absent from the export."
        },
        {
          "id": "source",
          "anchor": "source",
          "heading": "Source, tests, and version",
          "kind": "provenance",
          "required": true,
          "purpose": "Links to implementationPath, tests, engine guide, and the pinned enginePackageVersion.",
          "text": "Implementation packages/engine/src/canvas/replayInspector.ts. Tests: packages/engine/src/canvas/replayInspector.spec.ts and packages/engine/src/canvas/cyb-65-replay-inspector.repro.spec.ts. Guide: packages/engine/docs/replay-inspector.md."
        },
        {
          "id": "related",
          "anchor": "related",
          "heading": "Related features",
          "kind": "tutorial",
          "required": true,
          "purpose": "Prerequisites first, then relatedFeatureIds. Use manifest ids, not free text names.",
          "text": "Related: events, deterministic-mode, selection-trace. Prerequisites: none."
        },
        {
          "id": "advanced",
          "anchor": "advanced",
          "heading": "Advanced composition",
          "kind": "tutorial",
          "required": false,
          "purpose": "Optional: combining this feature with runtime groups, compositor, bindings, or scenarios.",
          "text": ""
        },
        {
          "id": "version",
          "anchor": "version",
          "heading": "Version provenance",
          "kind": "provenance",
          "required": true,
          "purpose": "Engine package version this page describes, when the API first appeared, and a warning if the live package.json has moved on.",
          "text": "Ships in @cyberart-io/engine 0.0.9. First appeared in 0.0.6."
        }
      ]
    },
    {
      "id": "selection-trace",
      "slug": "selection-trace",
      "route": "/docs/features/selection-trace",
      "title": "Selection trace",
      "purpose": "Why a binding, revision, asset, sequence, or fallback won, with secret-safe export.",
      "status": "substantial",
      "navGroup": "host-testing",
      "packageName": "@cyberart-io/engine",
      "enginePackageVersion": "0.0.12",
      "compatibleEngineVersions": {
        "min": "0.0.9"
      },
      "documentationSource": {
        "kind": "site-page",
        "path": "src/docs/pages/selection-trace.ts"
      },
      "exampleIds": [],
      "relatedFeatureIds": [
        "presentation-bindings",
        "content-revision",
        "asset-resolver",
        "production-scenario",
        "replay-inspector"
      ],
      "prerequisiteFeatureIds": [],
      "aliases": [],
      "keywords": [
        "selection-trace",
        "createSelectionTrace",
        "attachSelectionTrace",
        "recordSelectionDecision"
      ],
      "surfaces": [
        "browser",
        "headless"
      ],
      "primaryExports": [
        "createSelectionTrace",
        "attachSelectionTrace",
        "recordSelectionDecision"
      ],
      "agentRoute": "/docs/agent/features/selection-trace.json",
      "summary": "Why a binding, revision, asset, sequence, or fallback won, with secret-safe export.",
      "owningModule": "./canvas/selectionTrace",
      "lifecycle": {
        "status": "substantial",
        "introducedIn": "0.0.9",
        "compatibleEngineVersions": {
          "min": "0.0.9"
        },
        "normative": true,
        "experimental": false,
        "deprecated": false,
        "successorFeatureIds": []
      },
      "evidence": {
        "implementationPath": "packages/engine/src/canvas/selectionTrace.ts",
        "tests": [
          "packages/engine/src/canvas/selectionTrace.spec.ts",
          "packages/engine/src/canvas/cyb-85-selection-trace.repro.spec.ts"
        ],
        "engineGuide": "packages/engine/docs/selection-trace.md"
      },
      "redirectsFrom": [],
      "examples": [],
      "pageSections": [
        {
          "id": "problem",
          "anchor": "problem",
          "heading": "What this solves",
          "kind": "tutorial",
          "required": true,
          "purpose": "Name the problem the feature exists to solve.",
          "text": "Hosts need to know why a binding, revision, asset, sequence, or fallback won, with an export that cannot leak secrets."
        },
        {
          "id": "when-to-use",
          "anchor": "when-to-use",
          "heading": "When to use it",
          "kind": "tutorial",
          "required": true,
          "purpose": "Say when this is the right tool, and when a related feature is a better fit.",
          "text": "createSelectionTrace / attachSelectionTrace / recordSelectionDecision whenever presentation-bindings, content-revision, or asset-resolver make a choice. production-scenario attaches evidence; replay-inspector is the broader causation tree."
        },
        {
          "id": "conceptual-model",
          "anchor": "conceptual-model",
          "heading": "Conceptual model",
          "kind": "tutorial",
          "required": true,
          "purpose": "Objects, states, and how they relate. Diagrams optional; names must match the API.",
          "text": "Decision records: what won, why, what lost. Secret-safe export, attachable to production-scenario evidence."
        },
        {
          "id": "lifecycle",
          "anchor": "lifecycle",
          "heading": "Lifecycle",
          "kind": "normative",
          "required": true,
          "purpose": "Create / attach / run / snapshot / restore / destroy, including which events fire.",
          "text": "createSelectionTrace → attachSelectionTrace → recordSelectionDecision at each choice → export redacted."
        },
        {
          "id": "cart-vs-host",
          "anchor": "cart-vs-host",
          "heading": "Cart versus host",
          "kind": "normative",
          "required": true,
          "purpose": "What the cart may declare or emit versus what the host/runtime must provide. Do not blur the boundary.",
          "text": "The host attaches the trace and redacts. Carts/bindings record decisions. Tokens and credentials never belong in the export."
        },
        {
          "id": "contract",
          "anchor": "contract",
          "heading": "Minimal contract",
          "kind": "normative",
          "required": true,
          "purpose": "Smallest API/configuration surface that a correct integration uses, with types and defaults.",
          "text": "```ts\nimport { createSelectionTrace, attachSelectionTrace, recordSelectionDecision } from '@cyberart-io/engine';\n\nconst trace = createSelectionTrace();\nattachSelectionTrace(trace);\n```\n\nPrimary exports: createSelectionTrace, attachSelectionTrace, recordSelectionDecision."
        },
        {
          "id": "failure",
          "anchor": "failure",
          "heading": "Failure and fallback",
          "kind": "normative",
          "required": true,
          "purpose": "How the feature fails closed, what events/diagnostics look like, and what the host should do.",
          "text": "A missing reason still must not include secrets. Export fails closed if a forbidden key is present; do not skip recording fallbacks."
        },
        {
          "id": "accessibility",
          "anchor": "accessibility",
          "heading": "Accessibility",
          "kind": "normative",
          "required": true,
          "purpose": "Keyboard, focus, reduced motion, captions/non-audio alternatives, contrast, and anything the feature itself publishes (e.g. semantic regions).",
          "text": "Selection reasons can feed host diagnostics. They are not a user-facing caption unless the host chooses to surface a safe subset."
        },
        {
          "id": "example",
          "anchor": "example",
          "heading": "Runnable example",
          "kind": "example",
          "required": true,
          "purpose": "Link canonical example IDs from the manifest. Embed and exact source come from the lock; this section must not paste a drifting snippet.",
          "text": "No canonical example yet. packages/engine/docs/selection-trace.md and packages/engine/src/canvas/cyb-85-selection-trace.repro.spec.ts are the runtime proof."
        },
        {
          "id": "expected-result",
          "anchor": "expected-result",
          "heading": "Expected result",
          "kind": "example",
          "required": true,
          "purpose": "What a reader should see, hear, or be able to do after the example runs.",
          "text": "recordSelectionDecision stores why a winner won. Export omits secrets. Fallbacks are present in the trace."
        },
        {
          "id": "source",
          "anchor": "source",
          "heading": "Source, tests, and version",
          "kind": "provenance",
          "required": true,
          "purpose": "Links to implementationPath, tests, engine guide, and the pinned enginePackageVersion.",
          "text": "Implementation packages/engine/src/canvas/selectionTrace.ts. Tests: packages/engine/src/canvas/selectionTrace.spec.ts and packages/engine/src/canvas/cyb-85-selection-trace.repro.spec.ts. Guide: packages/engine/docs/selection-trace.md."
        },
        {
          "id": "related",
          "anchor": "related",
          "heading": "Related features",
          "kind": "tutorial",
          "required": true,
          "purpose": "Prerequisites first, then relatedFeatureIds. Use manifest ids, not free text names.",
          "text": "Related: presentation-bindings, content-revision, asset-resolver, production-scenario, replay-inspector. Prerequisites: none."
        },
        {
          "id": "advanced",
          "anchor": "advanced",
          "heading": "Advanced composition",
          "kind": "tutorial",
          "required": false,
          "purpose": "Optional: combining this feature with runtime groups, compositor, bindings, or scenarios.",
          "text": ""
        },
        {
          "id": "version",
          "anchor": "version",
          "heading": "Version provenance",
          "kind": "provenance",
          "required": true,
          "purpose": "Engine package version this page describes, when the API first appeared, and a warning if the live package.json has moved on.",
          "text": "Ships in @cyberart-io/engine 0.0.9. First appeared in 0.0.9."
        }
      ]
    }
  ],
  "examples": [
    {
      "id": "create-runtime-canvas",
      "route": "/docs/examples/create-runtime-canvas",
      "title": "Silent createRuntime canvas fill",
      "purpose": "Show the engine README quick start: mount a silent cart, fill the canvas from seed-driven state, and start the loop. No audio, network, or extra UI.",
      "featureIds": [
        "runtime"
      ],
      "runtimeKind": "render",
      "artifact": {
        "state": "available",
        "reason": "Cache verified against the lock."
      },
      "source": {
        "state": "restricted",
        "reason": "Source not published; example-carts is private.",
        "files": [
          "cart.ts",
          "main.ts",
          "manifest.json"
        ]
      },
      "provenance": {
        "repository": "cyberart-io/example-carts",
        "commit": "2ed0eb599ded827cca46bcd2fc66dea78f387a0c",
        "engineVersion": "0.0.12",
        "sourceUrl": null
      },
      "capabilities": {
        "permissions": [],
        "assets": [],
        "network": "none",
        "credentials": "none"
      },
      "interaction": {
        "mode": "none",
        "notes": "No pointer or keyboard. The cart starts immediately because it is silent."
      },
      "expected": {
        "visible": "The canvas is filled with an rgb color whose red channel is taken from the token seed hue and drifts each frame.",
        "audible": "silent"
      },
      "accessibility": {
        "notes": "No interactive controls. Docs embeds should caption the canvas as a seed-colored fill.",
        "fallback": "A text description of a solid color field that slowly changes is sufficient when canvas is unavailable."
      },
      "viewport": {
        "width": 320,
        "height": 180
      }
    },
    {
      "id": "calculation-capability",
      "route": "/docs/examples/calculation-capability",
      "title": "Calculation cart with capability manifest",
      "purpose": "Show a calculation cart that updates from host-channel events without a canvas, plus a versioned engine capability manifest the host can validate. Not a game.",
      "featureIds": [
        "calculation-carts",
        "capability-manifest",
        "headless-harness"
      ],
      "runtimeKind": "calculation",
      "artifact": {
        "state": "available",
        "reason": "Cache verified against the lock."
      },
      "source": {
        "state": "restricted",
        "reason": "Source not published; example-carts is private.",
        "files": [
          "cart.ts",
          "main.ts",
          "capabilities.json",
          "manifest.json"
        ]
      },
      "provenance": {
        "repository": "cyberart-io/example-carts",
        "commit": "2ed0eb599ded827cca46bcd2fc66dea78f387a0c",
        "engineVersion": "0.0.12",
        "sourceUrl": null
      },
      "capabilities": {
        "permissions": [
          "host.intent.*",
          "host.state.*"
        ],
        "assets": [],
        "network": "none",
        "credentials": "none",
        "engineCapabilityFile": "capabilities.json"
      },
      "interaction": {
        "mode": "scripted",
        "notes": "The host dispatches host.state.scene-changed. There is no pointer or keyboard surface."
      },
      "expected": {
        "visible": "No canvas. After a scene-changed event, cart state.sceneId matches the payload and the cart emits host.intent.overlay with overlayId banner.",
        "audible": "silent"
      },
      "accessibility": {
        "notes": "No visual surface. Docs should describe the event exchange in text.",
        "fallback": "State and emitted events are the accessible representation."
      },
      "viewport": {
        "width": 320,
        "height": 180
      }
    },
    {
      "id": "keyboard-inject",
      "route": "/docs/examples/keyboard-inject",
      "title": "Keyboard inject and key trace",
      "purpose": "Show KeyboardManager: register a single-character action, inject or press j, and paint a growing bar plus last-key fill. No audio, network, or credentials.",
      "featureIds": [
        "keyboard"
      ],
      "runtimeKind": "render",
      "artifact": {
        "state": "available",
        "reason": "Cache verified against the lock."
      },
      "source": {
        "state": "restricted",
        "reason": "Source not published; example-carts is private.",
        "files": [
          "cart.ts",
          "main.ts",
          "manifest.json"
        ]
      },
      "provenance": {
        "repository": "cyberart-io/example-carts",
        "commit": "2ed0eb599ded827cca46bcd2fc66dea78f387a0c",
        "engineVersion": "0.0.12",
        "sourceUrl": null
      },
      "capabilities": {
        "permissions": [],
        "assets": [],
        "network": "none",
        "credentials": "none"
      },
      "interaction": {
        "mode": "keyboard",
        "notes": "Press j after Run. Headless tests call harness.key('j')."
      },
      "expected": {
        "visible": "A light bar grows across a dark canvas each time j is received. After the first j the background shifts to blue-gray.",
        "audible": "silent"
      },
      "accessibility": {
        "notes": "The initiating key is j. Docs should say so next to the embed. The bar width is the hit count.",
        "fallback": "Text: hits increment when j is injected; lastKey becomes j."
      },
      "viewport": {
        "width": 320,
        "height": 180
      }
    },
    {
      "id": "audio-unlock",
      "route": "/docs/examples/audio-unlock",
      "title": "Audio unlock control",
      "purpose": "Show the host unlock gesture for the shared audio graph. This cart paints a speaker glyph and emits no PCM. Run is the unlock control; the caption is the non-audio alternative.",
      "featureIds": [
        "audio"
      ],
      "runtimeKind": "render",
      "artifact": {
        "state": "available",
        "reason": "Cache verified against the lock."
      },
      "source": {
        "state": "restricted",
        "reason": "Source not published; example-carts is private.",
        "files": [
          "cart.ts",
          "main.ts",
          "manifest.json"
        ]
      },
      "provenance": {
        "repository": "cyberart-io/example-carts",
        "commit": "2ed0eb599ded827cca46bcd2fc66dea78f387a0c",
        "engineVersion": "0.0.12",
        "sourceUrl": null
      },
      "capabilities": {
        "permissions": [],
        "assets": [],
        "network": "none",
        "credentials": "none"
      },
      "interaction": {
        "mode": "none",
        "notes": "Run is the unlock control. There is no extra cart UI."
      },
      "expected": {
        "visible": "A two-rectangle speaker glyph on a dark field.",
        "audible": "Unlock click arms the shared audio graph; this cart emits no PCM. The caption is the transcript."
      },
      "accessibility": {
        "notes": "Canonical caption is expected.audible. Run is the explicit unlock control.",
        "fallback": "Text: Run unlocks the page audio graph. This example does not play a tone."
      },
      "viewport": {
        "width": 320,
        "height": 180
      }
    },
    {
      "id": "snapshot-restore",
      "route": "/docs/examples/snapshot-restore",
      "title": "Snapshot export and restore",
      "purpose": "Show versioned snapshots: ticks grow a strip; exportSnapshot then importSnapshot restores the saved width. Reset remounts at zero. No audio, network, or credentials.",
      "featureIds": [
        "snapshots"
      ],
      "runtimeKind": "render",
      "artifact": {
        "state": "available",
        "reason": "Cache verified against the lock."
      },
      "source": {
        "state": "restricted",
        "reason": "Source not published; example-carts is private.",
        "files": [
          "cart.ts",
          "main.ts",
          "manifest.json"
        ]
      },
      "provenance": {
        "repository": "cyberart-io/example-carts",
        "commit": "2ed0eb599ded827cca46bcd2fc66dea78f387a0c",
        "engineVersion": "0.0.12",
        "sourceUrl": null
      },
      "capabilities": {
        "permissions": [],
        "assets": [],
        "network": "none",
        "credentials": "none"
      },
      "interaction": {
        "mode": "none",
        "notes": "State changes every frame. Tests export/import. Docs Reset remounts."
      },
      "expected": {
        "visible": "An orange strip grows with ticks. After importSnapshot the strip jumps back to the saved width. Reset remounts at zero.",
        "audible": "silent"
      },
      "accessibility": {
        "notes": "Strip width is the tick count. Restore vs live should be obvious. Reset returns to the default state.",
        "fallback": "Text: ticks increment each frame; importSnapshot restores the saved tick count."
      },
      "viewport": {
        "width": 320,
        "height": 180
      }
    }
  ],
  "redirects": [
    {
      "from": "/",
      "to": "/gallery",
      "kind": "relocate",
      "note": "MainPage + CartFeed is the homepage today. After CYB-92, `/` is the framework landing and the feed moves to `/gallery`. Do not delete the gallery surface. Target requires sign-in; anonymous visitors see a sign-in gate.",
      "status": "active"
    }
  ],
  "plannedRedirects": [],
  "deprecations": [],
  "omittedModules": [
    {
      "path": "packages/engine/src/math.ts",
      "status": "internal",
      "reason": "Not re-exported from index.ts or headless.ts."
    },
    {
      "path": "packages/engine/src/browser.ts",
      "status": "internal",
      "reason": "Vendor rAF / DPR helpers used inside the runtime, not a public entry export."
    },
    {
      "path": "packages/engine/src/canvas/animationManager.ts",
      "status": "internal",
      "reason": "Runtime internals. Public cart type is AnimationCart from animation.ts."
    },
    {
      "path": "packages/engine/src/canvas/mainCanvas.ts",
      "status": "internal",
      "reason": "Canvas mount helper. Public host API is createRuntime."
    },
    {
      "path": "packages/engine/src/canvas/cartStateCodec.ts",
      "status": "internal",
      "reason": "Codec used by persistence; public contract is the snapshot envelope."
    },
    {
      "path": "packages/engine/src/canvas/cartFramebufferStore.ts",
      "status": "internal",
      "reason": "Framebuffer cache for save/load. Not exported."
    },
    {
      "path": "packages/engine/src/canvas/hostChannel.ts",
      "status": "internal",
      "reason": "Implementation module. HostChannel is exported from cyberArtRuntime (runtime)."
    },
    {
      "path": "packages/engine/src/canvas/deterministicMode.ts",
      "status": "internal",
      "reason": "Implementation of deterministic mode. Public names (clocks, ScriptedAction) ship through cyberArtRuntime."
    },
    {
      "path": "packages/engine/src/audio/workletPatch.ts",
      "status": "internal",
      "reason": "Tone.js worklet registration patch. Not on the public engine entry."
    },
    {
      "path": "packages/engine/src/audio/libraries.ts",
      "status": "internal",
      "reason": "AudioLibrarySpec is re-exported as a type on cyberArtRuntime, not this module path."
    }
  ],
  "contentHash": "443533f81a3bcac4bea709d794388786b96ea4f95149f05bfb7c14e12142f077"
}
