Skip to main content

Workspace Composition Decision Register

This register freezes the v0 architectural decisions for the Maya workspace composition platform. A decision is locked when it protects the responsibility split:

Host resolves runtime.
Composer composes structure.
Render runtime executes UI.
ClaudeWorkspace packages product defaults.
Modules are trusted React UI adapters, not state/runtime owners.

Reopening a locked decision requires one of these triggers:

  • a proven v0 implementation blocker
  • a security finding that invalidates the current boundary
  • a public API compatibility issue before first release
  • a product requirement that cannot be modeled with the current contribution layer
  • a measured performance or testability failure that cannot be fixed locally

Locked Decisions

WCP-ADR-001: Responsibility Split

Decision: modules declare contracts, the host resolves runtime, the composition kernel composes structure, and the render runtime executes UI.

Why locked: this prevents permission, rendering, routing, and product defaults from collapsing into one package.

Forbidden drift: @maya/assistant-ui resolving grants, the composition package importing React, modules deciding their own authorization state, or ClaudeWorkspace creating hidden global state.

Reopen only if: v0 cannot express a required contribution without crossing package boundaries and no narrower adapter solves it.

WCP-ADR-002: Headless Composition Package

Decision: @maya/assistant-composition must be headless.

Why locked: the composition kernel needs deterministic tests, SSR-safe structure output, and no executable UI surface.

Forbidden drift: React, DOM, CSS, fetch, storage, localStorage, deployment config, health checks, or product controllers inside the package.

Reopen only if: a non-UI structural requirement cannot be represented as serializable descriptors and diagnostics.

WCP-ADR-003: Opaque Render Handles

Decision: WorkspaceComposition carries typed opaque render handles, not React components or dynamic imports.

Why locked: structure must remain inspectable, serializable, policy-checkable, and separate from executable UI.

Forbidden drift: component references, lazy import functions, JSX, render callbacks, or global registry keys that auto-bind at import time.

Reopen only if: handle indirection creates an unsolved SSR or code-splitting blocker.

WCP-ADR-004: Scoped React Render Runtime

Decision: hosts pass an explicit scoped ReactRenderRuntime to AssistantWorkspace.

Why locked: multiple workspaces, tests, SSR, trust policy, preload policy, and diagnostics all need explicit wiring.

Forbidden drift: global render registries, import-time registration, package side effects, or automatic render pack discovery.

Reopen only if: explicit runtime wiring creates unacceptable developer friction after the ClaudeWorkspace facade exists.

WCP-ADR-005: Module Instance Is The Unit

Decision: instanceId is the enabled unit; packageId only identifies code origin.

Why locked: multi-instance cases such as staging/prod, multi-tenant sources, and multiple backends must not require a future ID model rewrite.

Forbidden drift: package-level singleton state, package-level contribution IDs, package-level storage ownership, or implicit instance IDs.

Reopen only if: instance-level modeling causes a measurable DX or bundle-size cost that cannot be hidden by helpers.

WCP-ADR-006: Module Contributions Are Namespaced

Decision: module-local IDs are namespaced by instanceId.

Why locked: module authors should not hand-author global IDs, and import order must never decide ownership.

Forbidden drift: implicit override by array order, auto-generated random IDs, or silent suffixing of conflicting route paths and renderer claims.

Reopen only if: the canonical namespace syntax blocks a required cross-package integration pattern.

WCP-ADR-007: Modules Do Not Own Persistent State

Decision: modules are UI adapters for host capabilities and may hold only transient UI state.

Why locked: persistence, audit, permissions, synchronization, and storage must remain host-governed.

Forbidden drift: generic moduleState, module-owned localStorage, module-owned credential stores, cross-refresh state inside modules, or module-driven backend calls outside host controllers.

Reopen only if: a state category is proven transient, non-sensitive, and impossible to keep ergonomic through host-owned controllers.

WCP-ADR-008: Grants Are UI Activation Contracts

Decision: frontend grants describe UI activation, not a security boundary.

Why locked: build-time trusted modules run with the host frontend's authority, so backend and host policy remain the real enforcement layer.

Forbidden drift: calling frontend grants "authorization" without backend enforcement, passing real secrets to modules, or relying on render runtime trust policy for data protection.

Reopen only if: a future sandboxed or server-enforced plugin model adds a real isolation boundary.

WCP-ADR-009: Build-Time Trusted React Modules

Decision: v0 targets developer-installed npm React modules imported explicitly by the host.

Why locked: this gives full React expressiveness while avoiding marketplace, sandbox, and remote code complexity.

Forbidden drift: end-user plugin marketplace, online installation, remote ESM, iframe sandbox, micro-app runtime, or unknown third-party code execution.

Reopen only if: the product explicitly shifts to untrusted external plugins after v0 is stable.

WCP-ADR-010: Explicit v0 Composition Policy

Decision: v0 policy supports only explicit hide, reorder, replace, pathAlias, and rendererOverride.

Why locked: the composer should be predictable and fixture-testable, not a rule engine.

replace means descriptor-level ownership replacement through explicit composition policy. It does not publish arbitrary built-in page replacement, page-internal patching, DOM selector hooks, or local slot injection in v0.

Forbidden drift: predicate DSLs, order-dependent hooks, arbitrary patches, implicit conflict resolution, or user-authored policy scripts.

Reopen only if: multiple real modules need the same missing policy primitive and explicit operations become repetitive.

WCP-ADR-011: No Local Built-In Page Patching

Decision: v0 does not support patching arbitrary parts of built-in pages.

Why locked: local patches create tight coupling to product internals and make Claude page refactors unsafe.

Forbidden drift: selectors into built-in DOM, slot IDs scattered inside product pages, or module APIs named after concrete settings dialog positions.

Reopen only if: a stable semantic surface can be defined without exposing internal page structure.

WCP-ADR-012: No Default Text Message Override In v0

Decision: v0 modules cannot replace ordinary user or assistant text message rendering.

Why locked: the Claude-like chat experience is the product's core surface and should not be destabilized during the module architecture migration.

Forbidden drift: catch-all assistant message renderer claims, default user renderer replacement, or regex-based transcript patching.

Reopen only if: message ownership, audit, accessibility, and fallback semantics are stable.

WCP-ADR-013: Semantic Configuration And Integration Surfaces

Decision: modules declare configuration and integration surfaces by purpose, not by concrete UI location.

Why locked: Settings, Customize, Admin, and future layout changes should not break module APIs.

Forbidden drift: APIs such as settingsDialogPanel as the only contract, or module code that assumes a specific host layout.

Reopen only if: a product surface needs a location-specific API that cannot be derived from semantic contribution metadata.

WCP-ADR-014: One Pipeline, Two Facades

Decision: ClaudeWorkspace and lower-level renderers must share one deterministic composition and render pipeline.

Why locked: the high-level DX path must not hide a separate runtime or produce behavior the low-level path cannot test.

Forbidden drift: ClaudeWorkspace bypassing the composer, global state inside convenience APIs, or product-only contribution paths.

Reopen only if: the low-level API cannot express a required first-party product behavior.

WCP-ADR-015: Diagnostics Are Product Inputs

Decision: diagnostics are stable, structured outputs from the composer and render runtime.

Why locked: module authors need actionable failure modes for instance, route, renderer, handle, policy, and grant issues.

Forbidden drift: human-only strings, swallowed conflicts, console-only errors, or diagnostics that cannot be asserted in tests.

Reopen only if: a diagnostics category proves internal-only and has no host, docs, or test value.

WCP-ADR-016: CSS Is Scoped To Assistant UI

Decision: module CSS must run under .aui-root, use --aui-* tokens where possible, and avoid global resets.

Why locked: trusted modules still share the host page and can visually break the workspace without CSS discipline.

Forbidden drift: unscoped body, html, button, input, *, or :root rules from modules, or automatic CSS injection by render pack side effects.

Reopen only if: a future packaging layer can prove CSS isolation with stronger tooling.

Reopen Process

A reopened decision must record:

  • decision ID
  • reopening trigger
  • affected packages
  • proposed replacement
  • migration impact
  • test impact
  • security impact
  • final owner approval

Owner approval follows .github/CODEOWNERS for .docs/architecture unless superseded by an explicit Linear project owner.

Until reopened, implementation issues must link to the relevant decision IDs and treat forbidden drift as out of scope.