OSS-first docs

These docs teach the open system first: contracts, generated surfaces, runtimes, governance, and incremental adoption. Studio shows up as the operating layer on top, not as the source of truth.

Build a first module bundle

Define one bundle spec, resolve it for a real route and preference profile, then render the resulting surface plan through the React host layer.

What you'll build

  • One `ModuleBundleSpec` with a route and workbench surface.
  • One `ResolvedSurfacePlan` from `resolveBundle`.
  • One React host using `BundleProvider` and `BundleRenderer`.

1) Define the bundle spec

The bundle spec is the durable contract. It owns route selection, surface shape, layouts, data recipes, and verification coverage for all seven preference dimensions.

src/bundles/support.workbench.bundle.ts
import { defineModuleBundle } from "@contractspec/lib.surface-runtime/spec/define-module-bundle";

export const SupportWorkbenchBundle = defineModuleBundle({
  meta: {
    key: "support.workbench",
    version: "0.1.0",
    title: "Support Workbench",
  },
  routes: [
    {
      routeId: "support-ticket",
      path: "/operate/support/tickets/:ticketId",
      defaultSurface: "ticket-workbench",
    },
  ],
  surfaces: {
    "ticket-workbench": {
      surfaceId: "ticket-workbench",
      kind: "workbench",
      slots: [
        { slotId: "primary", role: "primary", accepts: ["entity-section"], cardinality: "many" },
        { slotId: "assistant", role: "assistant", accepts: ["assistant-panel"], cardinality: "many", mutableByAi: true, mutableByUser: true },
      ],
      layouts: [
        { layoutId: "balanced", title: "Balanced", root: { type: "slot", slotId: "primary" } },
      ],
      data: [
        { recipeId: "ticket-core", source: { kind: "entity", entityType: "support.ticket" }, hydrateInto: "ticket" },
      ],
      verification: {
        dimensions: {
          guidance: "Surface can reveal walkthrough or inline help states.",
          density: "Layout supports compact and balanced detail modes.",
          dataDepth: "Resolver can hydrate summary or detailed ticket context.",
          control: "Advanced actions stay policy-gated.",
          media: "Surface supports text-first and hybrid assist modes.",
          pace: "Transitions and confirmations adapt to operator pace.",
          narrative: "Summary and evidence ordering stay explicit.",
        },
      },
    },
  },
});

Expected output: `defineModuleBundle` validates the route, surface, and verification dimensions at runtime.

2) Resolve the plan

Resolve against a real route, device, and preference profile. This is where the runtime chooses the surface, layout, bindings, and audit reasons.

src/runtime/resolve-support-workbench.ts
import { resolveBundle } from "@contractspec/lib.surface-runtime/runtime/resolve-bundle";

export const supportWorkbenchPlan = await resolveBundle(
  SupportWorkbenchBundle,
  {
    tenantId: "tenant_demo",
    workspaceId: "workspace_ops",
    actorId: "user_42",
    route: "/operate/support/tickets/123",
    params: { ticketId: "123" },
    query: {},
    device: "desktop",
    locale: "en",
    preferences: {
      guidance: "hints",
      density: "compact",
      dataDepth: "detailed",
      control: "advanced",
      media: "hybrid",
      pace: "balanced",
      narrative: "top-down",
    },
    capabilities: ["tickets.read", "tickets.update"],
  }
);

Expected output: a `ResolvedSurfacePlan` with `bundleKey`, `surfaceId`, `layoutId`, `bindings`, `adaptation`, and audit reasons.

3) Render the plan

The host app renders the resolved plan, not the raw spec. That keeps layout selection, overlays, and AI proposals downstream of the declared bundle contract.

src/app/support/SurfaceHost.tsx
import { BundleProvider, BundleRenderer } from "@contractspec/lib.surface-runtime/react";

export function SurfaceHost() {
  return (
    <BundleProvider plan={supportWorkbenchPlan}>
      <BundleRenderer assistantSlotId="assistant" />
    </BundleProvider>
  );
}

Expected output: the route renders according to the resolved layout root and slot plan instead of bespoke page assembly.

Verification checks

  • The resolved `surfaceId` matches the route you expected.
  • The `layoutId` is stable for the active view and preferences.
  • The plan carries audit reasons and all seven adaptation dimensions.
  • Assistant or overlay work stays within declared slots.