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.

Spec pack

Module bundles turn surface composition into a typed ContractSpec runtime instead of a pile of bespoke page code.

The implemented package is @contractspec/lib.surface-runtime. It lets you define a bundle spec once, resolve it into a personalized surface plan, then render that plan through React without letting AI or per-route custom code bypass the declared system boundary.

Runtime promisebundle spec → resolved surface plan

The bundle spec owns what can render, where it can render, which preferences matter, and how overlays and AI proposals stay bounded.

Bundle capability

declared routes, surfaces, layouts, slots, actions, and data recipes

Bundle capability

entity registries and field renderers for relation-heavy workbenches

Bundle capability

preference-aware adaptation across guidance, density, data depth, control, media, pace, and narrative

Bundle capability

auditable overlays and bounded AI patch proposals instead of free-form JSX generation

1) Define the bundle

Start with a typed route and one surface. The runtime validates that you declared routes, surfaces, and verification coverage for all seven preference dimensions.

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" }],
      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.",
        },
      },
    },
  },
});

2) Resolve and render the plan

The app renders the resolved plan, not the raw spec. That keeps layout choice, data hydration, overlays, and AI proposals auditable.

SurfaceHost.tsx
import { BundleProvider, BundleRenderer } from "@contractspec/lib.surface-runtime/react";
import { resolveBundle } from "@contractspec/lib.surface-runtime/runtime/resolve-bundle";

const plan = 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"],
});

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

Adoption loop

  1. Define one `ModuleBundleSpec` with `defineModuleBundle` and keep the route and surface map explicit.
  2. Resolve the bundle with `resolveBundle` for a real user, route, device, and preference profile.
  3. Render the plan through `BundleProvider` and `BundleRenderer` so the UI stays downstream of the resolved spec.
  4. Add overlays, policy hooks, planner proposals, and telemetry only after the base route resolves cleanly.
  5. Pilot one dense domain first, such as a PM or operations workbench, before expanding the abstraction across the app.

Guardrails that matter

  • Do not stuff this behavior back into `lib.ui-kit`.
  • Do not let AI invent undeclared components or mutate raw DOM state.
  • Keep third-party UI libraries behind adapter subpaths only.
  • Every surface still needs explicit verification coverage for the seven preference dimensions.

Continue with overlays and runtime architecture

Once the bundle resolves deterministically, move into safe customization and the wider architecture that surrounds it.

OSS docscore-modelStart with OSS. Adopt Studio when you want the operating layer.

Why ContractSpec

Keep educational and comparison content reachable without letting it define the primary OSS learning path.