Workspace Composition Namespace, Diagnostics, And Versioning
This document is the canonical v0 spec for WCP-003: Write namespace, diagnostics, and versioning spec.
It locks the machine-readable contracts needed by @maya/assistant-composition, render runtimes, module packages, and future conformance tests.
Primary linked decisions:
WCP-ADR-003: opaque render handlesWCP-ADR-006: module contributions are namespacedWCP-ADR-015: diagnostics are product inputs
Adjacent decisions referenced by the registry:
WCP-ADR-004: scoped React render runtimeWCP-ADR-010: explicit v0 composition policyWCP-ADR-012: no default text message override in v0
Scope
This spec owns:
- canonical ID grammar
- contribution namespace grammar
- route path validation and effective path conflict semantics
- renderer claim grammar and conflict semantics
- structured diagnostic object semantics
- diagnostic code registry
- schema version field names
- golden fixture case inventory
This spec does not own:
composeWorkspace()result object shape, which belongs to WCP-005- full
CompositionPolicyJSON shape, which belongs to WCP-005 and WCP-010 - React render runtime behavior, which belongs to WCP-006
- trust, grant redaction, and security enforcement policy, which belongs to WCP-004
- exported TypeScript API implementation, which starts in WCP-007, WCP-008, WCP-009, WCP-010, and WCP-011
Canonical Namespace Grammar
The unit of enablement is a module instance, not a package.
canonicalContributionKey = `${ownerId}:${contributionKind}/${localId}`
ownerId is:
- a module
instanceIdfor module-owned contributions - a built-in owner id for product-owned contributions
Built-ins must use the same owner model as modules. They are not special merge cases.
type ContributionOwner = {
ownerKind: 'module' | 'builtin';
ownerId: string;
packageId?: string;
packageVersion?: string;
};
instanceId
instanceId identifies one enabled module instance.
Grammar:
instanceId = lower-alnum *( lower-alnum | "-" )
Rules:
- must start with lowercase ASCII letter or digit
- may contain lowercase ASCII letters, digits, and hyphens
- length must be 3 to 64 characters
- must not contain colon, slash, dot, whitespace, uppercase letters, or random suffixes
- must be globally unique within one composed workspace
Examples:
- valid:
data-lab-prod - valid:
github-staging - valid:
mcp2 - invalid:
DataLab - invalid:
data/lab - invalid:
data:lab - invalid:
dl
Built-In Owner IDs
Built-in owner ids use the reserved prefix builtin-.
Examples:
builtin-claude-chatbuiltin-claude-projectsbuiltin-claude-settings
Modules must not use an instanceId starting with builtin-.
contributionKind
contributionKind identifies the contribution collection.
Allowed v0 values:
routessidebar-itemsconfiguration-surfacesintegration-surfacesmessage-renderersmessage-appenderstool-renderersactivity-renderersfallback-surfaces
New contribution kinds require a decision or spec update before implementation.
localId
localId is only unique inside one owner and contribution kind.
Grammar:
localId = lower-alnum *( lower-alnum | "-" | "." )
Rules:
- must start with lowercase ASCII letter or digit
- may contain lowercase ASCII letters, digits, hyphens, and dots
- length must be 1 to 80 characters
- must not contain colon, slash, whitespace, uppercase letters, or random suffixes
- may repeat across different module instances
Examples:
- valid:
home - valid:
connection - valid:
tool.query-result - invalid:
ToolCard - invalid:
settings/panel
Canonical Contribution Keys
Examples:
data-lab-prod:routes/home
data-lab-prod:configuration-surfaces/connection
github-staging:tool-renderers/search-result
builtin-claude-chat:routes/chat
Rules:
- duplicate canonical contribution keys are errors
- same
localIdacross differentinstanceIdvalues is valid - import order must never resolve duplicate ownership
- automatically generated random IDs are forbidden
Route Paths
Route paths are user-visible URL paths inside the workspace.
Grammar:
routePath = "/" | "/" path-segment *( "/" path-segment )
path-segment = lower-alnum *( lower-alnum | "-" | "_" )
Rules:
- must start with
/ - must not end with
/, except the root path/ - must use lowercase ASCII letters, digits, hyphens, underscores, and slashes only
- must not contain repeated slashes
- must not contain dot segments, backslashes, percent encoding, protocol, or host
- must not include query strings, hashes, URL parameters, or dynamic route syntax
- host-owned routers may map params and search state outside the contribution key
- module routes should not claim
/in v0; root is reserved for product-kit built-ins unless explicit host policy allows it
Normalization:
- v0 route declarations must already be canonical
- the composer does not lowercase, trim, percent-decode, collapse repeated slashes, or add suffixes
pathAliaschanges the effective path used for conflict checks, but it does not change the canonical contribution key- path comparison is exact after validation and policy application
Examples:
/data-lab/data-lab/runs/github-staging
Invalid examples:
data-lab/DataLab/data-lab//data-lab?tab=runs/projects/:id/data//lab/data.lab/data/%6cabshttps://example.com/data-lab
Renderer Claims
Renderer claims describe ownership of non-default rendering surfaces.
Claim grammar:
rendererClaim = `${rendererKind}:${claimValue}`
Allowed v0 renderer kinds:
messagetoolactivity
Claim value grammar:
claimValue = lower-alnum *( lower-alnum | "-" | "_" | "." | "/" )
Rules:
- ordinary default user and assistant text messages are not claimable in v0
- custom module message types must use
message:<type> - tool renderers must use
tool:<toolName> - activity renderers must use
activity:<activityKind> - duplicate claims are errors unless explicit
rendererOverridepolicy resolves them - claim values are exact protocol strings and are not forced into
localIdgrammar
Examples:
message:data-lab.resulttool:data_lab.queryactivity:data-lab.query.started
Invalid examples:
message:assistant-textmessage:user-texttool:*activity:
Message Appenders
Message appenders are contributions, not exclusive renderer claims.
Multiple appenders may target the same message type and position. They are ordered by explicit policy first and canonical contribution key second.
Appender contribution example:
data-lab-prod:message-appenders/related-artifacts
Appender target example:
targetMessageType = "assistant-text"
position = "after"
Message appenders still cannot replace default text rendering.
Module Author Example
Input:
const dataLabModule = {
packageId: '@maya/data-lab-module',
packageVersion: '0.1.0',
moduleApiVersion: '0.1',
instanceId: 'data-lab-prod',
contributions: {
routes: [
{
id: 'home',
path: '/data-lab',
renderHandle: {
renderHandleVersion: '0.1',
kind: 'route',
id: 'data-lab.home',
},
},
],
sidebarItems: [
{
id: 'home',
label: 'Data Lab',
routeKey: 'data-lab-prod:routes/home',
},
],
configurationSurfaces: [
{
id: 'connection',
purpose: 'connection',
renderHandle: {
renderHandleVersion: '0.1',
kind: 'configuration-surface',
id: 'data-lab.connection',
},
},
],
toolRenderers: [
{
id: 'query-card',
claim: 'tool:data_lab.query',
renderHandle: {
renderHandleVersion: '0.1',
kind: 'tool-renderer',
id: 'data-lab.query-card',
},
},
],
},
};
Generated keys and claims:
data-lab-prod:routes/home -> /data-lab
data-lab-prod:sidebar-items/home
data-lab-prod:configuration-surfaces/connection
data-lab-prod:tool-renderers/query-card
tool:data_lab.query
Version fields used by the platform:
moduleApiVersion = 0.1
compositionSchemaVersion = 0.1
renderHandleVersion = 0.1
diagnosticSchemaVersion = 0.1
Conflict Rules
The composer must produce deterministic diagnostics for conflicts.
Errors:
- duplicate
instanceId - invalid
instanceId - duplicate canonical contribution key
- invalid
localId - duplicate effective route path
- invalid route path
- duplicate renderer claim
- invalid renderer claim
- default user or assistant text renderer override attempt
- unsupported schema version
Policy may resolve only these v0 conflicts:
- route path conflicts through explicit
pathAlias - renderer claim conflicts through explicit
rendererOverride - contribution ownership replacement through explicit descriptor-level
replace
Policy must not resolve conflicts by:
- import order
- silent suffixing
- wildcard ownership
- DOM selectors
- local built-in page patching
Structured Diagnostics
Diagnostics must be JSON-serializable, deterministic, and testable.
Tests should assert structured fields, not prose messages.
type CompositionJsonPrimitive = string | number | boolean | null;
type CompositionJsonValue = CompositionJsonPrimitive | readonly CompositionJsonValue[] | CompositionJsonObject;
type CompositionJsonObject = { readonly [key: string]: CompositionJsonValue };
type WorkspaceDiagnostic = {
diagnosticSchemaVersion: '0.1';
code: WorkspaceDiagnosticCode;
severity: 'error' | 'warning' | 'info';
origin:
| 'composer'
| 'render-runtime'
| 'host-helper'
| 'module-conformance';
category:
| 'structure'
| 'namespace'
| 'route'
| 'renderer'
| 'version'
| 'policy'
| 'handle';
subjects: DiagnosticSubject[];
details: CompositionJsonObject;
decisionIds: string[];
docsAnchor: string;
message: string;
};
message is for humans. It is not the compatibility contract.
Diagnostic Subjects
type DiagnosticSubject = {
ownerKind?: 'module' | 'builtin' | 'host';
ownerId?: string;
packageId?: string;
contributionKind?: string;
localId?: string;
canonicalKey?: string;
routePath?: string;
rendererClaim?: string;
};
Rules:
- diagnostics about ownership conflicts must include all conflicting owners
- diagnostics about route conflicts must include declared and effective route paths when policy changes them
- diagnostics about version mismatches must include supported and received versions
- diagnostics must include linked decision IDs when the failure protects an architectural lock
Diagnostic Detail Shapes
Registry Required Details names use these stable shapes:
owner: oneDiagnosticSubjectowners: array ofDiagnosticSubject, sorted byownerKind, thenownerId, thencanonicalKeyinstanceId: module instance id stringselectedOwner: oneDiagnosticSubjectoverriddenOwners: array ofDiagnosticSubject, sorted byownerKind, thenownerId, thencanonicalKeyfield: version or input field name as a stringexpected: expected type, enum, or schema version set as a string or sorted string arrayreceived: received version or value as a stringsupported: sorted string array of supported versionsvalue: invalid value as a stringreason: stable reason code string, not prosecanonicalKey: canonical contribution key stringroutePath: declared route path stringeffectivePath: path after explicit policy applicationoriginalPath: route path before explicit policy applicationaliasedPath: route path after explicitpathAliaspolicy applicationrendererClaim: renderer claim key stringpolicyId: host-authored policy id stringtarget: canonical contribution key, renderer claim, oroperation:<id>targeted by policyuse: canonical renderer contribution key requested byrendererOverriderenderHandle: opaque render handle id or serialized handle summarykind: render handle kind stringprovider: JSON-safe render provider summary withowner,packageId, optionalpackageVersion,handle,loadKind, optionalmoduleId, and optionaltrustTier;loadKindiseagerorlazyfor valid providers andunsupportedonly for invalid provider-shape summaries; it must not include component functions, controllers, loaders, preload functions, errors, stacks, or raw handle data valuesproviders: array ofprovidersummaries in supplied pack/provider traversal order; diagnostics consumers must not treat this order as winner selectionadmissionReason: stable render admission block reason stringunsupportedReason: stable unsupported render provider reason string, one ofruntime-schema-version,render-handle-version, orprovider-shapefailurePhase: stable render load failure phase string, one ofprovider-preloadorlazy-loaddefaultRenderer: protected default renderer id string
Fixture tests must sort owner arrays before assertion. They must not assert the human message as the primary compatibility contract.
Diagnostic Code Registry
This registry is the only prose source for v0 diagnostic codes. Codes are never reused.
Changing a code's severity, required details, or category is a public contract change after v0 publication.
The Fixture IDs column names the canonical fixture for each code. Additional matrix fixtures may assert the same code when they cover a related edge case.
| Code | Severity | Origin | Category | Required Details | Fixture IDs | Decisions |
|---|---|---|---|---|---|---|
WCP-STRUCT-001 | error | composer | structure | field, expected, received, reason | fixture-invalid-input-shape | WCP-ADR-001, WCP-ADR-015 |
WCP-NS-001 | error | composer | namespace | instanceId, owners | fixture-duplicate-instance-id | WCP-ADR-006, WCP-ADR-015 |
WCP-NS-002 | error | composer | namespace | value, field, reason | fixture-invalid-instance-id | WCP-ADR-006, WCP-ADR-015 |
WCP-NS-003 | error | composer | namespace | canonicalKey, owners | fixture-duplicate-canonical-key | WCP-ADR-006, WCP-ADR-015 |
WCP-NS-004 | error | composer | namespace | value, field, reason | fixture-invalid-local-id | WCP-ADR-006, WCP-ADR-015 |
WCP-ROUTE-001 | error | composer | route | effectivePath, owners | fixture-duplicate-route-path | WCP-ADR-006, WCP-ADR-015 |
WCP-ROUTE-002 | error | composer | route | routePath, reason, owner | fixture-invalid-route-path | WCP-ADR-006, WCP-ADR-015 |
WCP-ROUTE-003 | info | composer | route | originalPath, aliasedPath, policyId, owner | fixture-route-path-alias | WCP-ADR-010, WCP-ADR-015 |
WCP-RENDERER-001 | error | composer | renderer | rendererClaim, owners | fixture-duplicate-tool-renderer-claim | WCP-ADR-003, WCP-ADR-015 |
WCP-RENDERER-002 | error | composer | renderer | rendererClaim, reason, owner | fixture-invalid-renderer-claim | WCP-ADR-003, WCP-ADR-015 |
WCP-RENDERER-003 | error | composer | renderer | rendererClaim, owner, defaultRenderer | fixture-default-text-renderer-rejected | WCP-ADR-012, WCP-ADR-015 |
WCP-RENDERER-004 | info | composer | renderer | rendererClaim, selectedOwner, overriddenOwners, policyId | fixture-renderer-override | WCP-ADR-010, WCP-ADR-015 |
WCP-VERSION-001 | error | composer | version | field, received, supported | fixture-unsupported-composition-schema-version | WCP-ADR-015 |
WCP-VERSION-002 | error | composer | version | field, received, supported, owner | fixture-unsupported-module-api-version | WCP-ADR-015 |
WCP-VERSION-003 | error | composer | version | field, received, supported, owner | fixture-unsupported-render-handle-version | WCP-ADR-003, WCP-ADR-015 |
WCP-VERSION-004 | error | host-helper | version | field, received, supported | fixture-unsupported-diagnostic-schema-version | WCP-ADR-015 |
WCP-VERSION-005 | error | composer | version | field, received, supported | fixture-unsupported-policy-schema-version | WCP-ADR-010, WCP-ADR-015 |
WCP-POLICY-001 | error | composer | policy | policyId, target, reason | fixture-invalid-policy-target | WCP-ADR-010, WCP-ADR-015 |
WCP-HANDLE-001 | error | render-runtime | handle | renderHandle, owner, kind | fixture-render-runtime-missing-handle | WCP-ADR-003, WCP-ADR-004, WCP-ADR-015 |
WCP-HANDLE-002 | error | render-runtime | handle | renderHandle, owner, kind, providers | fixture-render-runtime-duplicate-provider | WCP-ADR-003, WCP-ADR-004, WCP-ADR-015 |
WCP-HANDLE-003 | error | render-runtime | handle | renderHandle, owner, kind, provider, admissionReason | fixture-render-runtime-blocked-handle | WCP-ADR-003, WCP-ADR-004, WCP-ADR-015 |
WCP-HANDLE-004 | error | render-runtime | handle | renderHandle, owner, kind, provider, unsupportedReason | fixture-render-runtime-unsupported-provider | WCP-ADR-003, WCP-ADR-004, WCP-ADR-015 |
WCP-HANDLE-005 | error | render-runtime | handle | renderHandle, owner, kind, provider, failurePhase | fixture-render-runtime-load-failed | WCP-ADR-003, WCP-ADR-004, WCP-ADR-015 |
WCP-HANDLE-006 | error | render-runtime | handle | renderHandle, owner, kind, provider | fixture-render-runtime-render-failed | WCP-ADR-003, WCP-ADR-004, WCP-ADR-015 |
WCP-HANDLE-007 | error | render-runtime | handle | renderHandle, owner, kind | fixture-render-runtime-unknown-target-fallback | WCP-ADR-003, WCP-ADR-004, WCP-ADR-015 |
Diagnostic Examples
Duplicate route path:
{
"diagnosticSchemaVersion": "0.1",
"code": "WCP-ROUTE-001",
"severity": "error",
"origin": "composer",
"category": "route",
"subjects": [
{
"ownerKind": "module",
"ownerId": "data-lab-prod",
"packageId": "@maya/data-lab-module",
"contributionKind": "routes",
"localId": "home",
"canonicalKey": "data-lab-prod:routes/home",
"routePath": "/data-lab"
},
{
"ownerKind": "builtin",
"ownerId": "builtin-claude-chat",
"packageId": "@maya/claude-workspace",
"contributionKind": "routes",
"localId": "data-lab",
"canonicalKey": "builtin-claude-chat:routes/data-lab",
"routePath": "/data-lab"
}
],
"details": {
"effectivePath": "/data-lab",
"owners": [
{
"ownerKind": "builtin",
"ownerId": "builtin-claude-chat",
"canonicalKey": "builtin-claude-chat:routes/data-lab"
},
{
"ownerKind": "module",
"ownerId": "data-lab-prod",
"canonicalKey": "data-lab-prod:routes/home"
}
]
},
"decisionIds": ["WCP-ADR-006", "WCP-ADR-015"],
"docsAnchor": "diagnostic-code-registry",
"message": "Route path '/data-lab' is claimed by multiple active contributions."
}
Duplicate renderer claim:
{
"diagnosticSchemaVersion": "0.1",
"code": "WCP-RENDERER-001",
"severity": "error",
"origin": "composer",
"category": "renderer",
"subjects": [
{
"ownerKind": "module",
"ownerId": "data-lab-prod",
"packageId": "@maya/data-lab-module",
"contributionKind": "tool-renderers",
"localId": "query-card",
"canonicalKey": "data-lab-prod:tool-renderers/query-card",
"rendererClaim": "tool:data_lab.query"
},
{
"ownerKind": "module",
"ownerId": "warehouse-prod",
"packageId": "@maya/warehouse-module",
"contributionKind": "tool-renderers",
"localId": "query-card",
"canonicalKey": "warehouse-prod:tool-renderers/query-card",
"rendererClaim": "tool:data_lab.query"
}
],
"details": {
"rendererClaim": "tool:data_lab.query",
"owners": [
{
"ownerKind": "module",
"ownerId": "data-lab-prod",
"canonicalKey": "data-lab-prod:tool-renderers/query-card"
},
{
"ownerKind": "module",
"ownerId": "warehouse-prod",
"canonicalKey": "warehouse-prod:tool-renderers/query-card"
}
]
},
"decisionIds": ["WCP-ADR-003", "WCP-ADR-015"],
"docsAnchor": "diagnostic-code-registry",
"message": "Renderer claim 'tool:data_lab.query' is claimed by multiple active contributions."
}
Version Fields
The v0 platform uses explicit schema version fields instead of inferred package versions.
Required fields:
compositionSchemaVersion: version ofWorkspaceCompositionstructuremoduleApiVersion: version of module declaration and contract compatibilityrenderHandleVersion: version of opaque render handle grammar and semanticsdiagnosticSchemaVersion: version of diagnostic object schema
Initial v0 value:
0.1
Rules:
- version fields are strings
- compatibility is decided by an explicit supported-version set, not npm semver inference
- unsupported versions produce
WCP-VERSION-*diagnostics - package semver does not replace schema version checks
- schema versions do not authorize runtime or permission decisions
diagnosticSchemaVersionis emitted on diagnostics and validated by host helpers or persisted diagnostic readers
Ownership notes:
policySchemaVersionsemantics belong to WCP-005 and WCP-010; its diagnostic code is registered here so every diagnostic code remains centrally owned- render runtime package version policy belongs to WCP-006 and release governance
- full semver automation belongs to release work, not WCP-003
Golden Fixture Matrix
This matrix defines the cross-platform fixture inventory and expected diagnostic codes. It does not duplicate full expected JSON objects.
Package-specific specs may add fixture IDs for their own behavior, but they must not remove, rename, or redefine the fixture IDs in this table.
| Fixture ID | Intent | Expected Diagnostic Codes |
|---|---|---|
fixture-happy-single-module | one valid module instance | none |
fixture-happy-multi-instance | same package enabled twice with distinct instances | none |
fixture-same-local-id-across-instances | same local ID in different instances is valid | none |
fixture-invalid-input-shape | malformed top-level input, non-array contribution collection, missing required descriptor field, missing runtime, or invalid runtime status | WCP-STRUCT-001 |
fixture-duplicate-instance-id | duplicate module instance ID | WCP-NS-001 |
fixture-invalid-instance-id | invalid module instance ID | WCP-NS-002 |
fixture-duplicate-canonical-key | same owner, kind, and local ID twice | WCP-NS-003 |
fixture-invalid-local-id | invalid local contribution ID | WCP-NS-004 |
fixture-duplicate-route-path | two owners claim same effective route path | WCP-ROUTE-001 |
fixture-invalid-route-path | invalid route path grammar | WCP-ROUTE-002 |
fixture-route-path-trailing-slash-rejected | non-root trailing slash is rejected instead of normalized | WCP-ROUTE-002 |
fixture-route-path-alias | explicit path alias resolves conflict | WCP-ROUTE-003 |
fixture-route-path-alias-creates-conflict | explicit path alias creates a new effective path conflict | WCP-ROUTE-001 |
fixture-duplicate-message-renderer-claim | two owners claim same custom message type | WCP-RENDERER-001 |
fixture-duplicate-tool-renderer-claim | two owners claim same tool name | WCP-RENDERER-001 |
fixture-duplicate-activity-renderer-claim | two owners claim same activity kind | WCP-RENDERER-001 |
fixture-message-appenders-do-not-conflict | multiple appenders target same message and position | none |
fixture-invalid-renderer-claim | invalid renderer claim grammar | WCP-RENDERER-002 |
fixture-default-text-renderer-rejected | default text renderer override attempt | WCP-RENDERER-003 |
fixture-renderer-override | explicit renderer override resolves conflict | WCP-RENDERER-004 |
fixture-unsupported-composition-schema-version | unsupported composition schema version | WCP-VERSION-001 |
fixture-unsupported-module-api-version | unsupported module API version | WCP-VERSION-002 |
fixture-unsupported-render-handle-version | unsupported render handle version | WCP-VERSION-003 |
fixture-unsupported-diagnostic-schema-version | unsupported diagnostic schema version | WCP-VERSION-004 |
fixture-unsupported-policy-schema-version | unsupported composition policy schema version | WCP-VERSION-005 |
fixture-invalid-policy-target | policy points to missing or invalid target | WCP-POLICY-001 |
fixture-policy-conflict | policy contains ambiguous duplicate or conflicting operations | WCP-POLICY-001 |
fixture-policy-reorder-cycle | policy reorder constraints contain a cycle | WCP-POLICY-001 |
fixture-render-runtime-missing-handle | render runtime lacks required handle | WCP-HANDLE-001 |
fixture-render-runtime-duplicate-provider | render runtime receives duplicate providers for one owner plus handle | WCP-HANDLE-002 |
fixture-render-runtime-blocked-handle | render runtime admission policy blocks provider execution | WCP-HANDLE-003 |
fixture-render-runtime-unsupported-provider | render runtime rejects unsupported runtime or handle version | WCP-HANDLE-004 |
fixture-render-runtime-load-failed | render runtime lazy provider load fails | WCP-HANDLE-005 |
fixture-render-runtime-render-failed | render runtime component execution fails within one surface | WCP-HANDLE-006 |
fixture-render-runtime-unknown-target-fallback | render runtime receives an unknown target shape | WCP-HANDLE-007 |
fixture-runtime-ready | ready module contributes normally | none |
fixture-runtime-read-only | read-only module contributes active entries with read-only runtime metadata | none |
fixture-runtime-needs-configuration | needs-configuration module contributes configuration entry and fallback | none |
fixture-runtime-unauthorized | unauthorized module contributes fallback entry without default route or renderer UI | none |
fixture-runtime-disabled | disabled module omits non-fallback contributions and contributes matching fallback if declared | none |
fixture-runtime-error | errored module contributes default integration surfaces and matching fallback if declared | none |
Verification Rules
Now:
- docs verifier checks this spec exists
- docs verifier checks this spec is linked from the root docs indexes
- docs verifier checks required version fields are named
- docs verifier checks diagnostic registry codes are unique
- docs verifier checks diagnostic codes do not appear in other architecture docs
Later:
- WCP-009 implements namespace and conflict diagnostics
- WCP-010 implements explicit policy validation/application diagnostics
- WCP-011 implements
composeWorkspace()and golden fixture tests - WCP-015 and WCP-016 implement render runtime handle diagnostics
- release gates include public API snapshots for exported diagnostic and version types
Duplicate Truth Rule
This document is the only prose source for namespace, diagnostic code, and schema version semantics.
Other docs may say "structured diagnostics", "canonical namespace", or "golden fixture matrix", but they must not copy the diagnostic registry or redefine version field semantics.